lvalue methods

2009-10-20 Thread Jon Lang
I recently attempted to write a sample mutable role that made use of a
number of lvalue methods, and I had a bear of a time getting it to
work.  Could we arrange for a more intuitive option to be available?
For example, allow the programmer to pass a writer code block in
through the rw trait, and assume that the default codeblock is a
reader codeblock.  Something like:

method x() is rw( { $.x = $_ } ) { return $.x }

The idea is that if this is being called as an rvalue, the { return .x
} block would be called, but if it's being called as an lvalue, the {
.x = $_ } block would be called.

The above example is of course trivial.  A more serious example might
be one based off of a coordinate system:

role point {
has Num $x, Num $y;
method angle() is rw( { $.x = .r * cos($_); $.y = .r * sin($_)
} ) { return atn($.y/$.x) }
method r() is rw( { $.x = $_ * cos(.angle); $.y = $_ *
sin(.angle) } ) { return sqrt($.x * $.x + $.y * $.y ) }
}

This strikes me as being much more readable than the current approach
of explicitly returning a proxy object.  I'd even be fine if the above
were treated as syntactic sugar for the creation of a proxy object -
that is, have:

method x() is rw( { $.x = $_ } ) { return $.x }

be exactly equivalent to something like:

method x($val) is rw { return new Proxy:
FETCH = method { return $.x },
STORE = method { $.x = $_ }
}

...but without the programmer having to worry about how to access the
role's attributes from within the proxy object.

-- 
Jonathan Dataweaver Lang


Re: lvalue methods

2009-10-20 Thread David Green

On 2009-Oct-20, at 8:04 am, Jon Lang wrote:
The above example is of course trivial.  A more serious example  
might be one based off of a coordinate system:


   role point {
   has Num $x, Num $y;
   method angle() is rw( { $.x = .r * cos($_); $.y = .r *  
sin($_) } ) { return atn($.y/$.x) }
   method r() is rw( { $.x = $_ * cos(.angle); $.y = $_ *  
sin(.angle) } ) { return sqrt($.x * $.x + $.y * $.y ) }

   }

This strikes me as being much more readable than the current  
approach of explicitly returning a proxy object.  I'd even be fine  
if the above were treated as syntactic sugar for the creation of a  
proxy object -


And/or some sugar for using special STORE methods on a variable, e.g.:

has $angle is set { $.x = .r * cos($_); $.y = .r * sin($_) };

(Well, in this example that makes extra storage space for the $angle  
attribute which we don't actually want, but there are many cases where  
an easy way to override STORE is really what is useful rather than an  
lvalue sub.)


But one of the problems with lvalue subs that don't simply return a  
variable (or equivalently, my is set example) is that you can't say  
things like temp lvalue() unless temp is receiving an actual  
variable to work on.


In the case where angle() (or $.angle) is changing $.x and $.y, should  
trying to temporize it do temp $.x and temp $.y as well?  Should  
it be impossible?  Can Perl tell whether it should be impossible or  
not?  Does it need to be illegal to change other variables inside a  
STORE?



Meanwhile, the flip side to wanting an easy way to do is set is that  
often when someone reaches for an lvalue sub, all he really wants is a  
way to pass an arg to the sub that looks like assignment.  For example  
wanting foo($x) = $y to be a prettier way to write foo($x, $y).   
This could be handled by, say, having a special rvalue keyword in  
signatures, e.g.:


sub foo($x, rvalue $y?) { ... }

foo(42); # $y is undef
foo(42) = 24;# $y is 24
foo(42, 24); # syntax error

This has the advantage of often doing what people want, and the  
disadvantage of not working with temp, etc.  At least Perl could  
know that temp isn't allowed to work with such subs, though.  On the  
other hand, something that looks like an assignment ought to work like  
an assignment, including temp


Especially since if you want something that looks more assignment-y  
than passing a regular arg, we already have a way to do that, namely,  
using the == syntax to feed args into a slurpy parameter.  But in  
your angle example, we really do want an assignment because the net  
result is to assign stuff.  Perhaps method angle is setting ($.x,  
$.y) ... to indicate that whatever is done to angle should really  
affect $x and $y, and any other attributes that aren't specified may  
not be used.



-David