This may all be explained in the upcoming A12, but I'm trying to get
Accessors figured out in my head, and I had a few questions and comments.
===== paraphrased from Damian in http://www.nntp.perl.org/group/perl.perl6.language/9576)
it seems very likely that if you write:
class Foo { # version 1
has $.bar_attr is public;
}
then you'll get an automagically created lvalue accessor method that will
allow you to write:
my $foo = Foo.new();
$foo.bar_attr = 1;
and then later define a real C<Foo::bar_attr> method to encapsulate it:
class Foo { # version 2
has $.bar_attr is public;
method bar_attr is rw($rvalue) () {
if exists $rvalue {
croak "Negative value" if $rvalue < 0;
$.bar_attr = $rvalue;
}
return $.bar_attr;
}
}
=====
From Damian's comments I conclude:
There will be one autogenerated accessor, with the same name as the
attribute (not get/set pairs).
It will work correctly in all these cases:
$x = $foo.bar_attr; # read
$foo.bar_attr = $x; # write
$x = $foo.bar_attr = $y; # write then read
My question is about these cases:
$foo.bar_attr += $z # read then write
$foo.bar_attr++;
L<Apocalypse 6/Lvalue subroutines> would seem to apply here because of the
"is rw" trait. It says I will need to return a proxy object in order for
these cases to work right, because I only get to return from the accessor
once, and subsequent writes are performed on the proxy object.
In fact, I think Damian's example above (written long before A6, so not
his fault) would fail to croak if I wrote:
$foo.bar_attr = 12;
$foo.bar_attr -= 100;
According to A6, it should probably be written like this:
method bar_attr() is rw {
return my $bar_proxy is Proxy(
for => $.bar_attr,
STORE => {
croak "Negative value" if $_ < 0;
$.bar_attr = $_;
},
);
}
But if we *always* have to return a proxy object, in case someone uses +=
on our attribute, I can see users yearning for the simplicity of get/set,
even if it is not as syntactically sweet.
I really like the "is Proxy" for tie-ing things, but I keep thinking its a
bit of overkill for accessors. From an easy-things-should-be-easy
perspective, I wouldn't mind at all if $object.attr += 3 magically
arranged to call an accessor twice: once to read, and once to write.
The accessor would have a simple signature:
method bar(?$val) {...}
It is simple to know if the accessor is reading or writing:
method bar(?$val) {
print "Ima reader" if want;
print "Ima writer" if exists $val;
}
But I am asking for magic with that, because $foo.bar is no longer what it
looks like (a method with no arguments). "$foo.bar += 3" is transformed
into "$for.bar($foo.bar() + 3)". I may lose things like the ability to
bind, etc.
A proxy should be able to do anything, it just doesn't do it as simply as
I want. So it occurs to me that it might be possible to implement the one
in terms of the other. To do that I need to explore another area from the
upcoming apocalypse (traits), so forgive my deficient syntax.
I want to get from here
method bar_attr(?$val) is accessor {
$.bar_attr = $val if exists $val;
return $.bar_attr;
}
to here
method bar_attr() is rw {
return my $x is Proxy (
for => $.bar_attr,
FETCH => { $.bar_attr },
STORE => { $.bar_attr = $_ },
);
}
using a break-the-rules trait called accessor.
I'm not sure what method gets called when a trait is applied to something,
so I will assume that APPLY is called with the something it is applied to
as the invocant.
trait accessor {
my $proxy is Proxy;
method APPLY( Code $acc : ) {
# "Proxy" is a trait/role with FETCH and STORE attributes (?)
$proxy.FETCH = { $acc() };
$proxy.STORE = { $acc($^a) };
# Proxy.for is not set, since I do not know what is being
# proxied; I only know how to access it.
# add the rw trait
$acc but= rw;
# wrap the applyee
$acc.wrap( sub (?$val) {
$proxy = $val if exists $val; # so $foo.bar(1) still works
return $proxy;
} );
}
}
I *think* that accomplishes my goal of simple-to-write accessors (for
scalars, at least). Now I only have to change the signature in Damian's
example, and I think it will work for $foo.bar_attr -= 9999999;
method bar_attr(?$rvalue) is accessor {
if exists $rvalue {
croak "Negative value" if $rvalue < 0;
$.bar_attr = $rvalue;
}
return $.bar_attr;
}
Or maybe I'm missing the point completely.... comments? rebuttals?
~ John Williams