[% setvar title Lvalue subroutines: implicit and explicit assignment %]

This file is part of the Perl 6 Archive

Note: these documents may be out of date. Do not use as reference!

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

TITLE

Lvalue subroutines: implicit and explicit assignment

VERSION

  Maintainer: James Mastros <james@rtweb.net>
  Date: 24 Aug 2000
  Mailing List: perl6-language-subs@perl.org
  Number: 149
  Version: 1
  Status: Developing

ABSTRACT

This RFC proposes two parallel methods for subroutines to be used as lvalues: implicit and explicit assignment. These methods are similar to the methods proposed previously in RFCs 107 and 118.

DESCRIPTION

Implicit Assignment

This is very similar to the lvalue subs in perl5.6, and to the method recommended in RFC 107.

It allows code such as

        package Object;
        sub foo(\%inner) : method {
                return %inner{foo};
        }

to be used as the lvalue in an assignment. It also allows it in a mutator context with single-evalutation, and allows it to be dynamically assigned to.

This requires, however, that the return statement not return the value of %inner{foo}, which is, after all, just a value and therefore cannot be assigned to, but rather a reference thereto.

Therefore, the definition of the return function must be changed such that it is lazy.

Additionally, the definition of assignment must not normally evaluate a lazy expression, but rather evaluate it to the point where its value would become the value of a variable, and then assign to that variable. If the expression cannot be evaluated to that point, (for example, $x+3), then it may be an error to assign to it.

(The may be is there because we might try solving the equation of the assignment until one side becomes the value of a variable, and then assigning the value to the variable such that it becomes true. This is almost certainly what is meant by the original expression. However, this almost certainly isn't worth the effort required.)

(This process could also be expressed in terms of automatic enreferencing and dereferencing, but this way seems more perlish.)

Given the definition of Object::foo above, the code $obj->foo = 42; should result in this process:

The context of the LHS is propagated from the RHS. This implies that the RHS is evaluated first.

A lvalue sub using implicit assignment may be local()ized or safe()d using the normal methods.

Explicit Assignment

In this form of lvalue sub, the sub is passed both it's LHS arguments and it's RHS value.

To indicate that the subrotuine may be called with explicit assignment, one or more of the elements of its parameter list must be declared with a :lvalue attribute.

The context of the RHS is determined by the declared calling convention of the lvalue sub. If and only if the lvalue sub is defined to take one scalar argument (not including a forced-reference [?]), then the RHS is evaluated in a scalar context. Otherwise, it is evaluated in a list context.

If more explicit contexts are added, then the context of the RHS exactly matches that specified by the :lvalued elements of the LHS' parameter list.

Given the code

        sub mode(\%self; $mode :lvalue) {
         if ($mode <5 && $mode >= 0) {
          %self{mode} = $mode;
         } else {
          carp "Mode outside of range 0..5!";
         }
        }

The code mode($foo) = 3; executes by calling mode with %self = %$foo and $mode = 3. The code mode($foo) = qw(3 4 5) has the same effect, since qw(3 4 5) is it's lengh in scalar context. The code mode($foo) executes by calling mode wih %self = %$foo and $mode undef (it would be an error had the semicolon been a comma).

Note that the RHS is evaluated first, since it's context can be gathered from the signature of the lvalue sub.

Mutators are called with the lvalue part being the result of calling the function first in a rvalue context lazaly.

Thus, the code mode($foo)++ results in a call to mode($foo, lazy mode($foo)+1);. It is the lvalue sub's responsibility to make certain that single-evaluation semantics are followed if it wishes to do so by decomposing the lazy parameter. Such decomposition is outside the realm of this RFC.

It is not possible to local()ize or safe()ty an explicit-assignment style lvalue sub in a manner transparent to that sub. For that reason, a closure returning the current rvalue of the lvalue sub is created into a temporary. The temporary is then evaluated and it's value assigned back to the call at then end of the block (but only if the block exits cleanly in the case of a safe() assignment).

Thus, given the definition of mode above,

        {
         local mode($foo) = 1;
         ...;
        }

Results in code equivalent to

        {
         my $temp = sub { mode($foo) }; #create a closure
         mode($foo) = 1;
         ...;
         mode($foo) = &$temp; #evaluate the closure
        }

Note that this results in three calls to the sub in response to one explicit assignment. This may be a very bad thing in some cases, but is unavoidable.

Miscellany

A subroutine may be marked as being in error to call in a lvalue context by specifying a :nonlvalue attribute on it. This prevents it from being called as an implicit lvalue function.

REFERENCES

RFC 118: lvalue subs: parameters, explicit assignment, and wantarray() changes: This is the basis for our explicit assignment section.

RFC 107: lvalue subs should receive the rvalue as an argument: This is the basis for our implicit assignment section.

RFC 132: subroutines should be able to return an lvalue: We, in effect, always make the value returned by return a valid lvalue, so that the same code can be used for both the rvalue and lvalue cases. This is in direct contradiction to the lreturn keyword recommended here.

RFC 130: Transaction-enabled variables for Perl6: We discuss how lvalue subs can be safe()d in the Implementation sections.

RFC 128: Subroutines: Extend subroutine contexts to include named parameters and lazy arguments: We use the syntax defined here.

"return" in perlfunc: We extend the definition of return given here.