class Dog;
has $.name is rw;
has $.age is rw;
has $.gender is rw;
I initially decide to accept the default accessors.
$dog.name = 'Ralph';
print $dog.age;
This works well for a while, but then I decide to update Dog so that setting
the name also sets the gender.
$dog.name = 'Susie'; # also sets $dog.gender to 'female'
How do I write such a name() method? Do I just check the arg, set the
gender, and then return $.name as an "lvalue" or something?
If so, what happens if, some time down the road, the $.name attribute goes
away entirely? I can't return it as an lvalue now, can I?
Basically, I'm wondering how much of the object's internals I'm exposing by
accepting the default accessors.
-John
IIRC, something like
has $.name is rw is STORE { ... };
> If so, what happens if, some time down the road, the $.name attribute goes
> away entirely? I can't return it as an lvalue now, can I?
Why not?
Juerd
> -----Original Message-----
> From: John Siracusa [mailto:sira...@mindspring.com]
> Sent: Monday, 19 April, 2004 02:21 PM
> To: Perl 6 Language
> Subject: A12: default accessors and encapsulation
>
>
> Let's say I have a class with some attributes:
>
> class Dog;
>
> has $.name is rw;
> has $.age is rw;
> has $.gender is rw;
>
> I initially decide to accept the default accessors.
>
> $dog.name = 'Ralph';
> print $dog.age;
>
> This works well for a while, but then I decide to update Dog so
> that setting
> the name also sets the gender.
>
> $dog.name = 'Susie'; # also sets $dog.gender to 'female'
>
> How do I write such a name() method? Do I just check the arg, set the
> gender, and then return $.name as an "lvalue" or something?
class Afghan
is Dog
{
does Hunter;
does AKC;
has $.name is rw
will STORE { .set_name($^name); };
has $.taliban is OppressiveGovernment;
method set_name($self: String $name) {
DESTROY unless $.taliban.approves($name);
given ($name) {
when <Names::Female> {
...
}
...
}
}
}
>
> If so, what happens if, some time down the road, the $.name attribute goes
> away entirely? I can't return it as an lvalue now, can I?
method name(String ?$name) {...}
>
> Basically, I'm wondering how much of the object's internals I'm
> exposing by accepting the default accessors.
Since lvalue subs/methods are easy, and $obj.attr looks like $obj.method
with no args, it should be pretty trivial to drop in a replacement if you
decide you don't like the attribute-based solution.
You are a little committed, since <$dog.name = "fred"> does assume that
you've got a reference to something you can pass back, but that's about it.
You can leave the "name" attribute around as a placeholder and let the STORE
block update the "official" location, or you could return some sort of
proxy-lvalue object that wasn't really a part of Dog:
class WeaselDog
is Dog
{
class PhonyAttr
{
$.store;
$.fetch;
method STORE { $.store($^value); return $_; }
method FETCH { return $.fetch(); }
}
method set_name(String $name) {...}
method get_name() {...}
method name is rw {
return new PhonyAttr(
object => $_,
store => { .set_name $^name; },
fetch => { .get_name; }
);
}
}
=Austin
The "will STORE" stuff covers the easy cases, but can I extend it all the
way up to a name() that's a multimethod with a ton of optional args? I
supposed you can (technically) do all of that with "will STORE", but it
seems an odd place for what would more naturally be code in the name()
method itself.
> You can leave the "name" attribute around as a placeholder and let the STORE
> block update the "official" location, or you could return some sort of
> proxy-lvalue object that wasn't really a part of Dog
Heh, getting progressively more scary :)
From the point of view of the person coding the new, fancier name() method,
it would be nice if some magic would make all existing calls to
$dog.name = 'foo';
look like this inside the new name() method
$dog.name('foo');
but I supposed that really hoses the meaning of "=" :)
The alternate techniques suggested are powerful, but they also strike me as
slightly heroic. I can imaging using them to patch or extend some existing
code, but starting a Perl 6 class from scratch, I'd really have to think
about the costs of using the default accessors at all.
One work-around might be an alternate kind of default accessor that doesn't
allow assignment:
$dog.name # get
$dog.name('foo') # set
$dog.name = 'foo' # compile-time error
That is a lot more directly (and simply) "future-proof" than the "is rw"
accessor.
I'd either like a way to more cleanly extend the default accessor's
assignment behavior down the road (i.e. by just writing a new name() method,
not by hacking away at STORE traits and adding private worker subs) or a way
to auto-generate the slightly more "boring" default accessor shown above.
I'd prefer the former since I'm hoping to avoid the likes of
Class::MethodMaker as long as possible in the world of Perl 6 :)
In the absence of both, I can imaging that 5 years into the life of 6PAN, a
substantial portion of the Perl 6 modules will have STORE hooks on what they
originally thought would be "simple" attributes that don't need full-blown
accessors... :)
-John
Yeah, that's exactly what I don't want to type over and over :) It's not
much better than the old Perl 5 standby:
sub name { @_ > 1 ? $_[0]->{'name'} = $_[1] : $_[0]->{'name'} }
since once I have to write something, the time and effort savings is pretty
much cancelled out.
-John
I think a role on the attribute is not the right place to put it. What
you're doing is returning a proxy object that knows how to set both the
name and the gender. Here are a couple of implementations:
class Dog {
has $.name;
has $.gender;
method name() {
return my $dummy
is Proxy(
for => $.name,
STORE => sub ($in) {
$.gender = /<Names::Female>/ ?? 'male' :: 'female';
$.name = $in;
},
);
}
}
Yuck. Much nicer:
class Dog {
has $.name;
has $.gender;
method name()
will get { $.name }
will set -> $in {
$.gender = /<Names::Female>/ ?? 'make' :: 'female';
$.name = $in;
}
{ }
}
This is nothing new. So, for fun, here's the implementation of C<get>
and C<set>:
role get {
multi sub trait_auxiliary:is(get $trait, &code: ?$arg) {
wrap &code: {
my $result := call;
return my $dummy
is Proxy(
for => $result,
FETCH => $arg,
);
};
}
}
role set {
multi sub trait_auxiliary:is(set $trair, &code: ?$arg) {
wrap &code: {
my $result := call;
return my $dummy
is Proxy(
for => $result,
STORE => $arg,
);
};
}
}
Luke
> I'd either like a way to more cleanly extend the default accessor's
> assignment behavior down the road (i.e. by just writing a new name() method,
> not by hacking away at STORE traits and adding private worker subs) or a way
> to auto-generate the slightly more "boring" default accessor shown above.
There *is* a way to do the latter. In A12, Larry implies that the declarators
in the body of a class definition are actually method calls on an instance of
MetaClass:
http://www.perl.com/pub/a/2004/04/16/a12.html?page=3#use_of_classes
http://www.perl.com/pub/a/2004/04/16/a12.html?page=19#new_grammatical_categories
So, presumably, by defining a new C<scope_declarator:has>, you would be able
to override the default accessor-generating behaviour.
The least scary way to do this would be to encapsulate it in a trait that is
applied to (and has its way with ;- the class declaration:
class Dog is getset {
has $.name is rw;
...
}
or perhaps is applied instead to individual attribute declarations:
class Dog {
has $.name is getset;
...
}
Alternatively, you could just *cheat* and define a macro (putting it in a
lexically-scoped module for convenience and safety):
module GetSet;
macro getset is export(:MANDATORY)
is parsed(rx:words/ <?Perl6.type>? <?Perl6.attr_var>/)
($attr)
{
my $accessor = substr $attr{attr_var}, 2;
return "method set_$accessor ($attr{type} $attr{$attr_var}) {} "
~ "method get_$accessor () { return $attr{attr_var} } "
~ "has $attr{type} $attr{attr_var}"
;
}
and then:
class Dog {
use GetSet;
getset $.name;
...
}
Damian
I really don't understand what you're getting at here. First you
complain that you'd rather write an ordinary method, and then you
complain that you have to. Have I met someone lazier than me? :-)
Larry
I think we already have this. Just define a non-rw attribute and then
add your own writer as a multi-method.
has Str $.name;
multi method name(Str $n) {$.name = $n;}
--
Mark Biggar
mark.a...@comcast.net
Possibly :) Here's what I'm saying. In the first version of a class, there
will probably be a lot of simple get/set attributes. It's convenient not to
have to write any explicit methods for those.
If I accept the default accessors that you get "for free" when a class "has
$.foo is rw", then that means the users of my class can do $obj.foo =
whatever in order to set the foo attribute.
That's fine...until, some day down the road, I decide that getting and/or
setting the foo attribute needs to do something a bit more complex. Or
maybe I remove the $.foo attribute all together and replace it with a
collection of other attributes or something.
I can do that by writing my own foo() method, but there's the problem of all
the existing code out there that says $obj.foo = whatever. In order to
continue to support that, I have to provide an lvalue. As has been pointed
out, I can use "will STORE" to add the "extra" behavior while continuing to
use $.foo (or a proxy object, yadda) as my lvalue. But I find that slightly
kludgy.
If the default accessor had instead been defined like (the Perl 6
equivalent) of this:
sub name { @_ > 1 ? $_[0]->{'foo'} = $_[1] : $_[0]->{'foo'} }
then I wouldn't have any existing code doing $obj.foo = whatever to worry
about. So extending my foo() accessor by writing my own method would be
straight-forward.
Another alternative is to have a simpler, more direct way to continue to
support the $obj.foo = whatever semantics by writing a particular kind of
foo() method. I don't like piling the "side effects" into a "will STORE"
trait, since in version 4.0 of my class there could be a ton of code hanging
off that hook. It just looks like the wrong place to put that. I'd rather
have all the fancy things done when I call $obj.foo() defined in method foo
{ }.
A final alternative is to never use the default accessor that I get when my
class "has $.foo is rw" because that means having to support $obj.foo =
whaever semantics forever and ever. But that means I have to write (the
Perl 6 equivalent of) this:
sub name { @_ > 1 ? $_[0]->{'foo'} = $_[1] : $_[0]->{'foo'} }
for every single attribute. That's tiresome.
Basically, I'm wondering exactly who is the target audience for the default
accessor? It seems to tie the API to a very confining set of behaviors, and
expose more of the internals of a class than I'd like.
If $ob.foo will really never expand beyond a simple get/set interface to an
attribute, it works fine. But that's rarely true in the long term, IME. It
also, I think, creates an artificial distinction between those "simple"
attributes that users can expect to set with $obj.foo = whatever, and the
more complex attributes that must be set with $obj.foo(whatever). I'd like
more symmetry in my APIs, but then I'm faced with the options describe
above, none of which are as appealing as I'd like.
I think others will also value API symmetry and will therefore either create
their own "old-style" accessors everywhere, or (more likely) go to heroic
measures to support $obj.foo = whatever everywhere, forever, probably by
dangling 80% of their attribute accessor methdod code off of "will STORE"
hooks by the time version 4.0 cof their class rolls around. That's not
pleasant, IMO.
-John
Ooo, that would be neat.
> The least scary way to do this would be to encapsulate it in a trait that is
> applied to (and has its way with ;- the class declaration [...] or perhaps is
> applied instead to individual attribute declarations
Either of those looks okay...well, maybe "Damian okay" which is a slightly
different standard ;)
> Alternatively, you could just *cheat* and define a macro
That's a bit much for me. I mean, yeah, sure, you can basically do
"anything" that way, but come on. I dread what you will do with (or is that
"to") Perl 6 once it's released... ;)
-John
That's a bit too "example-specific." Really what I was getting at was
arbitrary, simple extensibility to do "anything" in response to $obj.foo
(with and without args) down the road, not just to do the specific thing
that I described in my example.
With a "boring" Perl 5 style get/set accessor API, I can just rewrite method
foo { } until the cows come home. I find that a lot more straight-forward
than the role playing you describe, but YMMV :)
-John
$obj.foo = 'bar'
winds up invoking a method on $obj with 'bar' as an argument, rather
than invoking a method on $obj that returns an lvalue to which
'bar' is then assigned. (Yes, like Ruby's def foo=; I have a Rubyometer
and I'm not afraid to use it!) It doesn't need to be the default
rw accessor behavior, but I would like it to be accomplishable without
jumping through a lot of hoops.
-Mark
It sounds a lot like C#'s properties, which are in my opinion one of the
best things in C#. Nice easy syntax for those as well, although I can't
remember it well enough right now to give an example, as I don't
generally keep a C# reference lying around at work where I never use it.
Effectively though, if you have an object foo with a property bar,
things like
foo.bar = 5;
seem to get turned into
foo.set_bar(5);
where set_bar() was pulled from the property declaration's set {} block,
where some magic goes on.
However, it would appear that what John is asking for is already
possible, just not necessarily particularly obviously, as returning a
proxy object to act as the lvalue and then do the appropriate magic
would work, but seems a little mucky. Even if that's what happens behind
the scenes.
On the other hand, just as a thought from a crazy C++er, couldn't you
accomplish a similar effect by defining your $.foo attribute to be of a
custom class, then overriding the = operator for that class...
I know, messy messy. Don't go that way. I shouldn't even have thought of it.
A problem with
$obj.foo = 'bar';
getting converted to
$obj.set_foo('bar');
(or whatever) is that set_foo may not have lvalue semantics. That is, it may
not behave appropriately by returning an lvalue object for use in cascading
assignment constructs:
$obj.foo = $obj.baz = 'bar';
Hanging the behavior off C<will STORE> and/or C<will FETCH> has the benefit
of P6 doing some or all of the work for you.
has $.foo will STORE { .:set_foo($^value); }
submethod :set_foo($new_foo) {...}
...
$obj.foo = 'bar';
A better solution to your scenario is to 6PAN a new MetaClass that does what
you want:
use AccessorClasses;
accessor_class Object
{
has $.foo is rw;
method :set_foo($new_foo, ?$optional_extra) {...}
method :get_foo() {...}
}
A simple, easily bundled grammar mod that automatically generates the C<will
STORE> logic for you, provided that you follow a simple xxx_ method naming
convention.
=Austin
Okay, well, I thought that my example did that, but apparently using
C<will get> and C<will set> is a little too complex... (my sentiments
are beginning to follow Larry's, in that I'm not sure you know what you
want -- perhaps you could give a hypotheical syntax?)
So, what you really want is a way so that:
$obj.foo = 'bar';
Is translated into:
$obj.foo('bar');
When you get rid of C<$.foo> and replace it with a C<foo> method.
This is doable (without a syntax munge), but it requires some
cleverness. First I'll do it by hand, then I'll wrap it up in a trait.
Please realize that all this I'm doing is an *implementation* of a very
easy usage.
class Dog {
class :FooProxy {
has $.method;
multi sub infix:= (FooProxy $proxy, Any $val) {
$.method($val);
}
}
method foo_call (?$arg) {
# accessor code here
}
method foo ($self:) is rw {
FooProxy.new(method => { $self.foo_call($_) })
}
}
That should turn:
$obj.foo = 'bar';
Into:
$obj.foo('bar');
And now we want to wrap that up so we can just say C<is accessor>:
role accessor {
class :Proxy {
has $.method;
multi sub infix:= (Proxy $proxy, Any $val) {
$.method($val);
}
}
multi sub trait_auxiliary:is (accessor $a, &code is rw: ?$arg) {
my sub access (::_ $self:) {
Proxy.new(method => { code($self: $_) })
}
&code = &access;
}
}
There. Now here's the important part: in order to *use* all this, you
import whatever module defines it, and then say:
class Dog {
method foo (?$arg) is accessor {
# accessor code here
}
}
If that's not easy enough for you, well, you're probably out of luck.
Luke
It would be even easier if we could put the read-accessor-code and
write-accessor-code in different methods.
class Dog {
multi method foo { ... }
multi method foo ($arg) is accessor { ... }
}
With this syntax, either method could be simply be omitted to create a
read-only or write-only accessor. The tricky part is getting the trait
on foo($) to override foo() correctly.
~ John Williams
Ugh! That's what I originally suggested, and it was shot down it was.
My first solution to your problem introduced the traits C<get> and
C<set>, allowing you to write it like this:
class Dog {
method foo ()
will get { ... }
will set { ... }
{ } # usually empty
}
I guess I bogged down that message with the implementation, so the
result may have been easy to miss.
Luke
That is what happened in my case. Apologies; it looks like your
original solution would do the job nicely. As long as the requisite
module comes standard I'd be satisfied. :)
--
Mark REED | CNN Internet Technology
1 CNN Center Rm SW0831G | mark...@cnn.com
Atlanta, GA 30348 USA | +1 404 827 4754
There are a couple of solutions, each with some syntax. Here they are, in
no particular order:
* Provide a second kind of default accessor
class Foo;
has $.foo is accessor; # or whatever, pick a name
With that "has" line alone, you auto-magically get an accessor that works
like this:
$obj.foo # get value of $.foo
$obj.foo(5) # set $.foo = 5
Why this is useful: It doesn't require you to write anything other than the
"has" line, and it provides a clean, simple way to extend the foo() accessor
later in the life of the class by actually writing a foo() method. Unlike
the "is rw" default accessor, there's no fear that any code will exist that
says $obj.foo = whatever.
I also think it's a more sensible default than the "is rw" semantics
(*unless* the solution below is implemented). Training people to use the
default "is rw" accessors only to have to explain later about all the hoops
they will have to jump through in order to maintain that API when the foo
method gets more complex is not my idea of a good time.
* Provide a simple(r) way to easily support the $obj.foo = whatever behavior
in perpetuity for any method.
There are a few ways this could be done. One suggestion was to somehow
appropriately remap this:
$obj.foo = 5;
to this:
$obj.foo(5);
once foo() mutates from an "is rw" default accessor to a full-blown "method
foo(...) { }"
I briefly entertained this solution in one of my earlier posts, but I think
it's not a good idea. It's a good description of the type of behavior I
want, but I don't like futzing with infix:= in order to get it.
Another solution is to have a way to tell method foo() to yank its arg out
of the rvalue if called as an lvalue. I don't have any good ideas about
this syntax, but here's the overview:
# Version 1.0
class Foo;
has $.foo is rw;
...
$obj.foo; # get
$obj.foo = 5; # set
Then:
# Version 4.0
class Foo;
has $.foo; # or maybe $.bar or anything else
method foo(?$arg)
{
# The magic happens in the line below
$arg = the rvalue if(caller wants lvalue);
# Do all sorts of crazy stuff, possibly involving $.foo, possibly not
...
return something; # maybe $.foo, maybe not
}
$obj.foo; # get
$obj.foo(5); # set
$obj.foo = 5; # still works, equivalent to $obj.foo(5)
All the code for maintaining the ability to do $obj.foo = whatever is in the
foo() method itself. It's not hanging off the $.foo attribute, and there
may no longer even be a $.foo attribute anymore. The fact that a $.foo
attribute existed at all was an implementation detail in version 1.0 of
class Foo, IMO.
I think all of these solutions are simpler and cleaner than the STORE hooks,
proxy objects, roles, and macros that have been suggested (all of which seem
like they'd work, FWIW).
-John
Well, I think we've come full circle. The get/set is the same as the
presumed current implementation, just with different keywords:
class Dog {
has $.foo
will FETCH { ... }
will STORE { ... }
;
}
I'm not saying there is anything wrong with that, but John Siracusa is
asking for something different, I think. A simple accessor which looks
like a method without having to play with Proxy, FETCH, STORE, etc.
If it still looks like $obj.foo = 1 outside the class, that's good too.
(Delphi and C# can do it; why can't we?)
Getting the multi-methods foo()/foo($) to automagically act as the
reader/writer for a virtual foo attibute seems like it would fulfill
his requirements. foo() already works for the reader, but foo($) needs
some help to make $obj.foo = 1 call $obj.foo(1).
Here's a feeble attempt to do what I suggested:
role accessor {
has $.accessor = 1;
multi sub trait_auxiliary:is( accessor $trait, &meth(Any) ) {
my $class = &meth.meta.class;
class $class is extended {
# class methods take precedence, so install a method
# in the class "is default" so it gets called first
multi method $meth.meta.name () is default {
return my $proxy is Proxy (
STORE => { &meth($^val) },
FETCH => {
my ($reader) =
grep !$_.meta.default ,
$class.meta.getmethods;
&$reader;
},
);
}
}
$meth does accessor;
}
}
class Dog {
multi method foo { ... }
multi method foo ($arg) is accessor { ... }
}
my Dog $spot;
$spot.foo++;
~ John Williams
I don't care what syntactic sugar you put underneath, but if you expose
this interface to the user, it's fundamentally flawed. This is my
argument from the Apocalypse, and it hasn't changed. It's wrong
to introduce a fundamental asymmetry that breaks the contract that
an accessor can be used as a variable. The arguments to an accessor
should function as part of the "long name" of whatever it is you're
setting, whether it's concrete or abstract. Some of those arguments
may want to be optional, or have other weirdnesses that get introduced
as your interface develops. Having an extra argument on the end
just totally screws up that sort of extensibility.
So do whatever you like to the declarations, but make sure you preserve
the symmetry and extensibility of
$obj.foo(*@STUFF, *%NONSENSE) # get value of $.foo
$obj.foo(*@STUFF, *%NONSENSE) = 5 # set $.foo = 5
And while you're at it, make sure your syntactic sugar doesn't force
people to duplicate the code to process *@STUFF and *%NONSENSE in
their getter and setter.
Them's the specs.
Larry
Er, I think we have different definitions of "accessor." I'm perfectly
happy to never allow anyone to do $obj.foo = whatever. I just don't want to
write trivial methods that get and set an attribute behind the scenes.
-John
Works for me. I just want to make sure that it's easy to write classes
that preserve that interface when there *is* no $.foo within the
implementation of the class. After all, the point of abstraction is to
hide implementation details, so it shouldn't matter to the caller whether
or not the attribute exists. And the fact that it looks like it exists to
the caller shouldn't force the implementation to manufacture attributes
just because otherwise there's no lvalue to return for assignment purposes.
As a trivial example, imagine that I want a class Length that lets me do
conversions:
my $len = new Length(furlongs => 10)
say $len.feet ==> 6600.0132
$1en.feet = 6
say $len.meters ==> 1.8288
say $len.furlongs ==> 0.0090908909
Ideally, the implementation of this class should consist of a single
attribute to store the length (in whatever it considers to be the
canonical units), a conversion table, and a single
AUTOLOAD/method_missing/whatever method which does conversions based on
(a) the name by which its invoked and (b) whether it's invoked as an lvalue or
rvalue.
-Mark
Eh? Why would you never allow anyone to use a simple syntax?
$oldfoo = $obj.foo;
$obj.foo = 1;
What's the problem here? No having to write trivial methods, no
complexity.
What Larry's trying to get at is that:
$status = $thread_shared_data.status(lock=>1);
could be defined as a multi which gets the value of the status variable,
but also asserts that thread synchronization or some form of threadsafe
locking must be performed. You're not SETTING status, you're reading it,
but you are passing parameters to the read accessor. How do you do that
if parameters force a write?
--
Aaron Sherman <a...@ajs.com>
Senior Systems Engineer and Toolsmith
"It's the sound of a satellite saying, 'get me down!'" -Shriekback
C# does it as
public int buffersize {
get {
return my_buffer.length();
}
set {
//[1]
double sqrt=Math.sqrt(value); //value is a keyword in C#
// because of this feature.
if(Math.floor(sqrt) == sqrt) {
my_buffer=new byte[value];
}
else {
throw new InvalidArgumentException(
"Value is not a power of two"
);
}
}
}
How different is that from:
method buffersize()
will fetch { +$.buffer.bytes }
will store {
my $sqrt=$^v.sqrt;
die "$^v is not a power of two" unless int($sqrt) == $sqrt;
$.buffer = "\x[0]" x $^v;
}
{}
Or even (my suggestion):
method buffersize()
will store {
my $sqrt=$^v.sqrt;
die "$^v is not a power of two" unless int($sqrt) == $sqrt;
$.buffer = "\x[0]" x $^v;
}
{ +$.buffer.bytes }
?
Of course, the best way to implement that would probably be something like:
our type Buffer ::= ByteString where
{ int sqrt +.bytes == sqrt +.bytes };
has Buffer $.buffer;
But that's neither here nor there...
[1] I'm certain there's a more efficient way to do this test, probably
involving bit twiddling. Whatever.
--
Brent "Dax" Royal-Gordon <br...@brentdax.com>
Perl and Parrot hacker
Oceania has always been at war with Eastasia.
The problem is when foo() becomes a complex method some time in the future.
It's some work to "upgrade" foo from a simple "is rw" attribute to a
full-blown method while also maintaining the end user's ability to say
$obj.foo = whatever.
> What Larry's trying to get at is that:
>
> $status = $thread_shared_data.status(lock=>1);
>
> could be defined as a multi which gets the value of the status variable,
> but also asserts that thread synchronization or some form of threadsafe
> locking must be performed. You're not SETTING status, you're reading it,
> but you are passing parameters to the read accessor. How do you do that
> if parameters force a write?
If that's your situation, you can make status() a "getter" only and add a
set_status() "setter." Or you could make foo() say "if I get a single
scalar (i.e. non-pair) arg, then set, otherwise get with options." But
maybe that's as evil to some people as writing trivial get/set accessors is
to me :)
-John
Interesting. Here's the Delphi version, for comparison.
// the trivial attibute
SomeNumber : Integer;
// the virtual property
// read/write can refer to either a variable or a func/proc
property VirtWrite : Integer read SomeNumber write SetSomeNumber;
procedure SetSomeNumber( in : Integer );
function GetSomeNumber : Integer;
The big win being that one can replace the trivial attribute with a
property, and the interface stays the same.
The perl6 version of that would of course be
class Dog {
method getfoo returns Int {...};
method setfoo(Int $v) {...};
has Int $.foo will FETCH \&getfoo
will STORE \&setfoo;
}
I'm not saying there is anything wrong with that. I completely agree with
Larry that the correct interface for accessors is $x = $obj.foo = $y;
I'm mainly curious if there is an even simpler way to define the
implementation of the accessor than the above.
The simplest possible rw accessor is
method foo is rw { $.foo };
which basically just makes $.foo available to the outside. When you get
more complex and need to intercept the reading and writing, it's clear
that returning a Proxy object is the best way (so far proposed) to do the
right thing for ($x = $obj.foo = $y) and ($obj.foo++). For convenience
and to avoid being uncool-retro, one also wants to separate the accessor
into read and write multi methods.
multi method foo() returns Int {...};
multi method foo(Int $v) {...};
has Int $.foo will FETCH { .foo } # ignore the name conflict for now
will STORE { .foo($^value) };
The declaration of the "has" variable to delegate to the above methods now
becomes repetitive boilerplate code, which could be eliminated by a clever
trait. It also interferes with the obvious name for a multi method
reader.
foo() obviously works fine (by itself) as a reader, but foo($) needs some
help. Somehow foo() has to return a Proxy which delegates STOREs to
foo($), and FETCHes to itself. Since foo() works fine as a read-only
accessor by itself, a minimalist approach shouldn't require foo() to add a
trait. foo($) needs the help so it should get the trait.
So I propose (in the request-for-comments sense, not the "this should
go into perl6 core" sense) that given a trivial accessor:
has $.foo is rw;
the minimal replacement with full read/write control would be this:
multi method foo() {...};
multi method foo($v) is accessor {...};
(And the accessor trait somehow does the magic to define a new foo() which
returns a Proxy which delegates to the declared foo() or foo($).)
~ John Williams
Could this be written as:
method buffersize {
+$.buffer.bytes
} will store {
my $sqrt = $^v.sqrt;
...
}
Or does "will store BLOCK" really have to go before the main block?
Juerd
The syntax <rule>s in Apocalypse 6 require the main block to be at the
end. http://dev.perl.org/perl6/apocalypse/A06.html#The_sub_form
Sure
method buffersize
will do { +$.buffer.bytes }
will store { ... }
IIRC
-Scott
--
Jonathan Scott Duff Division of Nearshore Research
du...@lighthouse.tamucc.edu Senior Systems Analyst II
And if you override the accessor, you can:
multi method foo(Str $blah = undef) is rw($new) {
(my($old),$.foo)=($.foo,$blah//$new);
.update_the_world_in_some_cool_way();
return $old
}
and while that's verbose, it's only a bit more verbose than:
multi method foo(Str $blah) {
(my($old),$.foo)=($.foo,$blah);
return $old;
}
which is what you would have write ANYWAY to add your new functionality.
I don't understand this.
What's the $new doing?
And if you've only got one of them, do you need multi, or just an optional
argument?
method foo(Str ?$new) is rw {
my $old = $.foo;
if (defined($new)) {
$.foo = $new;
.update_the_world();
}
return $old;
}
It's what we on the cabal have been calling an out-of-band parameter.
We have at times contemplated them for various purposes, none of
which seem to have panned out. Instead we've ended up with things
like $CALLER::_ and such.
: What's the $new doing?
It looks to me like it's being a parameter that doesn't interfere
with the official signature.
: And if you've only got one of them, do you need multi, or just an optional
: argument?
It's just an optional argument, but...
: method foo(Str ?$new) is rw {
: my $old = $.foo;
: if (defined($new)) {
: $.foo = $new;
: .update_the_world();
: }
: return $old;
: }
...but it doesn't ruin the signature symmetry between getter and
setter like yours. It does share the misfeature that you have to
write special switching code internally to deal with it, which the
proxy solution automatically dispatches for you. It also assumes
you even want the old value, which you often don't, if the proxy is
destined to be the left side of an ordinary assignment in void context.
And, in fact, if it weren't in void context, you'd want the new value,
not the old one, which means you better hang onto that proxy...
I suppose in the cases where the attribute can serve as its own proxy,
we could have a hook that captures control on setting, and lets you
add side effects en passant without making you responsible to actually
set the variable like a STORE hook would. If you wanted to remove the
actual attribute you'd have to graduate it to a STORE closure on a
real proxy though (with whatever syntactic sugar makes that most
palatable).
Larry