David Green wrote:
> It seems overly complex to me, but perhaps I'm missing good reasons for such
> an approach.  I see lvalue subs mainly as syntactic sugar:
>    foo(42);          # arg using normal syntax
>    foo <== 42;       # arg using feed syntax
>    foo = 42;         # arg using assignment syntax
> Feeds are a way of passing values to a function, but work like assignment
> when used on a variable; assignment is a way of giving a value to a
> variable, so it should work like passing args when used on a function.  Then
> you can easily do whatever you want with it.
> In fact, it could work just like a feed, and pass values to the slurpy
> params, but I think assignment is special enough to be worth treating
> separately.  Maybe something like:
>    sub foo ($arg1, $arg2, [EMAIL PROTECTED], =$x) {...}
>    foo(6,9) = 42;       # the 42 gets passed to $x
> That example uses a leading "=" for the "assigned" param (parallel to the
> leading "*" for the slurpy param), but I'm not crazy about it for various
> reasons (and =$x refers to iteration in other contexts).

OK; my take on it:

An "lvalue sub" is a sub that can be assigned to - the operative word
being "can".  There's a reason why Perl 6 talks about "is rw" and "is
ro" so much, but has yet to (and may never) officially approach the
idea of "is wo".  You don't _have_ to assign to an lvalue sub in order
to use it.  As such, an lvalue sub needs to be written in such a way
that it can be called to assign a value or to return a value.  The
current "proxy object" approach makes this explicit by mandating
separate FETCH and STORE methods to handle the two uses.  This has the
benefit of a fully consistent mechanism for handling "assignable
routines": return an assignable object; if a value was going to be
assigned to the subroutine, it is instead assigned to the returned
object.  Simple, and straightforward.

And sometimes messy.  Let's consider the following alternative,
inspired by David's suggestion:

(I'm thinking aloud here, exploring possibilities and looking for
problems as I go.  Please bear this in mind.)

If a routine is rw, you may optionally define a single "slurpy scalar"
(e.g., '*$value') in its signature.  This scalar counts as the last
positional parameter, much like slurpy arrays and hashes must be
declared after all of the positional parameters have been declared.
You do not need to pass an argument to it; but if you do, you may do
so in one of two ways: through the usual arguments syntax, or via
assignment syntax.

If an assignable routine does not have a slurpy scalar in its
signature, it operates exactly as currently described in S06: it
returns something that is assignable, which in turn is used as the
lvalue of the assignment operator.  If the slurpy scalar is present in
the signature, then an attempt to assign a value to the sub passes the
value in through the slurpy scalar, while an attempt to read the value
that the sub represents doesn't pass anything to the slurpy scalar.
The routine then replaces the normal assignment operation and returns
a value in much the same way that the assignment operator would.

This approach could be functionally equivalent to the "proxy object"
approach, but with a potentially more user-friendly interface.  That

  sub foo (*$value) { yadda }

might be shorthand for something like:

  sub foo () is rw {
    return new Proxy:
      FETCH => method { return .doit() },
      STORE => method ($val) { .doit($val) },
      doit => method ($value?) { yadda }

Jonathan "Dataweaver" Lang

Reply via email to