[% setvar title Subroutines: Replace C with a generic C function %]

This file is part of the Perl 6 Archive

To see what is currently happening visit http://www.perl6.org/

TITLE

Subroutines: Replace wantarray with a generic want function

VERSION

  Maintainer: Damian Conway <damian@conway.org>
  Date: 6 Aug 2000
  Last Modified: 18 Sep 2000
  Mailing List: perl6-language@perl.org
  Number: 21
  Version: 2
  Status: Frozen

ABSTRACT

This RFC proposes extending the number of detectable calling contexts and replacing the limited (and misnamed) context detection of wantarray with a more precise and general mechanism.

DESCRIPTION

want LIST want

want returns a value or list of values indicating the calling context of the current subroutine (i.e. the subroutine in which want is called).

want may also be passed a list of values that describe aspects of the context in which the current subroutine has been called. In that case, its return value also indicates whether the specified contexts are currently valid.

When want is called without arguments in a scalar context:

	$primary_context = want;

it returns a string indicating the (primary) context in which the current subroutine was called: one of "VOID", "SCALAR", or "LIST.

When want is called without arguments in a list context:

        ($primary, $count, @secondary) = want;

it returns a list of at least two values, indicating the contexts in which the current subroutine was called. The first two values in the list are the primary context (i.e the scalar return value) and the expectation count (see "Expectation counts" below). Any extra contexts that want may detect (see "Valid contexts" below) are appended to these two items.

When want is called without arguments in a hashref context (see "Valid contexts" below):

        if (want->{LVALUE}) { ... }

        unless (want->{COUNT} < 2) { ... }

it returns a reference to a hash whose keys are the valid contexts plus the special key "COUNT". The values for the context keys are always 1; the value for the "COUNT" key is always the expectation count.

When want is called with arguments in a scalar context:

	$primary_context = want 'LIST', 2, 'LVALUE';

it checks whether all the specified contexts are valid for the current subroutine. If any is not currently valid, want returns undef. Otherwise it returns the primary context string (just as scalar want with no arguments does).

When want is called with arguments in a list context:

        ($primary, $count, @secondary) = want 'SCALAR', 'RVALUE', 'HASHREF';

it again checks whether all the specified contexts are valid for the current subroutine. If any is not currently valid, want returns an empty list. Otherwise it returns the same list of context values as it would have if called without arguments.

To summarize these five alternatives:

  • If want is itself called in a scalar context, it returns only the primary context string.
  • If want is itself called in a list context, it returns all known context information via a list.
  • If want is itself called in a hashref context, it returns all known context information via a reference to a hash.
  • If want is called with arguments, the contexts specified by those arguments must be valid for the current subroutine, or else want returns false (either undef or ()).

Expectation counts

The second value returned by want in a list context is the current subroutine's "expectation count". This is an integer indicating the number of return values expected by the subroutine's caller. For void contexts, the expectation count is always zero; for scalar contexts, it is always zero or one; for array contexts it may be any non-negative number.

If the context is expecting an unspecified number of return values (typically because the result is being assigned to an array variable), the expectation count is the largest representable integer (~0).

When want is called with a numeric value as an argument, it returns true only if the current subroutine's calling context requires at least that many values. For example:

	if (want 2) { return ($x, $y) }		# context wants >= 2 values
	else        { return ($x); }		# context wants < 2 values

Valid contexts

The various contexts that may be specified as arguments to want and may be returned by it (as list values or hashref keys) are:

  • 'LIST'
  • The subroutine was called in a list context:

            @vals = func();
            print func();
  • 'SCALAR'
  • The subroutine was called in a scalar context:

            $val = func();
    	print scalar func();
  • 'VOID'
  • The subroutine was called in a void context:

            func();
  • 'COUNT'
  • Only returned as a literal string when used as a hashref key. Normally passed to want -- and returned by it -- as an integer.

    	func();                   # COUNT == 0
    	scalar func();            # COUNT == 0
    	() = func();              # COUNT == 0
    
    	$x = func();              # COUNT == 1
    	if (func()) { ... }       # COUNT == 1
    	($x) = func();            # COUNT == 1
    
    	($x, $y) = func();        # COUNT == 2
    	($x, $y, $z) = func();    # COUNT == 3
    
    	@a = func();              # COUNT == ~0
    	($x,@a) = func();         # COUNT == ~0
  • 'HASH'
  • The subroutine was called in a context where its return value is assigned to a hash:

            %hash = func();
  • 'STRING'
  • The subroutine was called in a stringifying context:

            $val = $hash{func()};
            print func();
  • 'NUMBER'
  • The subroutine was called in a numerifying context:

            $val = func() * 2;
            $val = sin(func());
            $val = $array[func()];
  • 'INTEGER'
  • The subroutine was called in a numerifying context that requires an integer:

            $val = $array[func()];
            $val = "str" x func();
  • 'BOOLEAN'
  • The subroutine was called in a boolean context:

            if ( func() ) {}
            $val = func() || 0;
  • 'SCALARREF'
  • The subroutine was called in a context that expects a scalar reference:

            $val = ${func()};
            \$val = func();                 # possible typeglob replacement?
  • 'ARRAYREF'
  • The subroutine was called in a context that requires an array reference be returned:

            func()->[$index];
            push @{func()}, $val;
            \@array = func();               # possible typeglob replacement?
  • 'HASHREF'
  • The subroutine was called in a context that requires a hash reference be returned:

            func()->{key};
            @keys = keys %{func()};
            \%hash = func();                # possible typeglob replacement?
  • 'OBJREF'
  • The subroutine was called in a context that requires an object reference be returned:

            func()->method();
  • 'CODEREF'
  • The subroutine was called in a context that requires a code reference be returned:

            func()->();
            &{func()}();
            \&hash = func();                # possible typeglob replacement
  • 'IOREF'
  • The subroutine was called in a context that requires a filehandle reference be returned:

            print {func()} @data;
  • 'LVALUE'
  • The subroutine call is being used as an lvalue:

            func() = 0;
  • 'RVALUE'
  • The subroutine call is being used as an rvalue:

            $val  = func();
            @vals = func();
  • Classname
  • The subroutine was called in a context that expects to see an object of the named class returned:

            my Dog $spot = func();          # want('Dog') returns true

Example

        our ($data, @data) ;

        sub lastdata : lvalue {

                if ($DEBUG)
                {
                        my ($primary, $expcnt, @secondaries) = want;
                        print "lastdata called in $primary context\n;
			print "Also in contexts: @secondaries\n";
                        print "expected $expcnt return values\n";
                }

                return                  if want->{VOID};
                return $data            if want qw(LVALUE SCALAR);
                return "$data"          if want 'SCALAR', 'STRING
                return eval{$data+0}||0 if want 'SCALAR', 'NUMBER';
                return !!$data          if want 'SCALAR', 'BOOLEAN';

                if ((undef,$count) = want 'LIST')
                {
                        return @data if $count > @data;
                        return @data[0..$count-1];
                }
        }

Listing of possible context combinations:

        within &func, &want returns:                    when:
        ============================                    =====

        (VOID,   0,  RVALUE)                            ;func();

        (SCALAR, 1,  LVALUE)                            func() = $x;
        (SCALAR, 0,  RVALUE)                            ; scalar func();
        (SCALAR, 1,  RVALUE)                            $x = func();
        (SCALAR, 1,  RVALUE, STRING )                   $x .= func();
        (SCALAR, 1,  RVALUE, BOOLEAN)                   die if func();
        (SCALAR, 1,  RVALUE, NUMBER )                   $x + func();
        (SCALAR, 1,  RVALUE, NUMBER, INTEGER)           ~func(); $array[func()];
        (SCALAR, 1,  RVALUE, SCALARREF)                 ${func()};
        (SCALAR, 1,  RVALUE, ARRAYREF)                  func()->[$index];
        (SCALAR, 1,  RVALUE, HASHREF)                   func()->{key};
        (SCALAR, 1,  RVALUE, CODEREF)                   func()->(@args);
        (SCALAR, 1,  RVALUE, OBJREF)                    func()->method();
        (SCALAR, 1,  RVALUE, IOREF)                     print {func()} @data;
        (SCALAR, 1,  RVALUE, Dog)                       my Dog $spot = func();

        (LIST,   0,  RVALUE)                            () = func();
        (LIST,   1,  RVALUE)                            ($x) = func();
        (LIST,   ~0, RVALUE)                            ($x, @array) = func();
        (LIST,   ~0, RVALUE)                            @array = func();
        (LIST,   ~0, RVALUE)                            print func();
        (LIST,   ~0, RVALUE, HASH)                      %hash = func();

IMPLEMENTATION

Dammit, Jim, I'm a doctor, not an engineer!

REFERENCES

perlfunc (wantarray)

RFC 259: Builtins : Make use of hashref context for garrulous builtins