Re: A12: default accessors and encapsulation
On Fri, Apr 23, 2004 at 03:23:09PM -0400, Austin Hastings wrote: : > 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 : > } : : I don't understand this. 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
RE: A12: default accessors and encapsulation
> -Original Message- > From: Aaron Sherman [mailto:[EMAIL PROTECTED] > Sent: Friday, 23 April, 2004 03:12 PM > To: John Siracusa > Cc: Perl 6 Language > Subject: Re: A12: default accessors and encapsulation > > > On Tue, 2004-04-20 at 10:13, John Siracusa wrote: > > On 4/19/04 7:20 PM, Larry Wall wrote: > > > On Mon, Apr 19, 2004 at 06:53:29PM -0400, John Siracusa wrote: > > > : Yeah, that's exactly what I don't want to type over and over :) > > > > > > 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? :-) > > > > 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. > > 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 > } 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; }
Re: A12: default accessors and encapsulation
On Tue, 2004-04-20 at 10:13, John Siracusa wrote: > On 4/19/04 7:20 PM, Larry Wall wrote: > > On Mon, Apr 19, 2004 at 06:53:29PM -0400, John Siracusa wrote: > > : Yeah, that's exactly what I don't want to type over and over :) > > > > 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? :-) > > 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. 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. -- Aaron Sherman <[EMAIL PROTECTED]> Senior Systems Engineer and Toolsmith "It's the sound of a satellite saying, 'get me down!'" -Shriekback
Re: A12: default accessors and encapsulation
On Tue, Apr 20, 2004 at 10:25:04PM +0200, Juerd wrote: > Brent 'Dax' Royal-Gordon skribis 2004-04-20 12:58 (-0700): > > 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 } > > Could this be written as: > > method buffersize { > +$.buffer.bytes > } will store { > my $sqrt = $^v.sqrt; > ... > } Sure method buffersize will do { +$.buffer.bytes } will store { ... } IIRC -Scott -- Jonathan Scott Duff Division of Nearshore Research [EMAIL PROTECTED] Senior Systems Analyst II
Re: A12: default accessors and encapsulation
On Tue, 20 Apr 2004, Juerd wrote: > Brent 'Dax' Royal-Gordon skribis 2004-04-20 12:58 (-0700): > > 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 } > > 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? The syntax s in Apocalypse 6 require the main block to be at the end. http://dev.perl.org/perl6/apocalypse/A06.html#The_sub_form
Re: A12: default accessors and encapsulation
Brent 'Dax' Royal-Gordon skribis 2004-04-20 12:58 (-0700): > 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 } 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
Re: A12: default accessors and encapsulation
On 4/20/04 4:08 PM, Aaron Sherman wrote: > On Tue, 2004-04-20 at 15:40, John Siracusa wrote: >> On 4/20/04 2:37 PM, Larry Wall wrote: >>> It's wrong to introduce a fundamental asymmetry that breaks the contract >>> that an accessor can be used as a variable. >> >> 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. > > Eh? Why would you never allow anyone to use a simple syntax? > > $oldfoo = $obj.foo; > $obj.foo = 1; > > What's the problem here? 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
Re: A12: default accessors and encapsulation
John Williams wrote: 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?) 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 <[EMAIL PROTECTED]> Perl and Parrot hacker Oceania has always been at war with Eastasia.
Re: A12: default accessors and encapsulation
On Tue, 2004-04-20 at 15:40, John Siracusa wrote: > On 4/20/04 2:37 PM, Larry Wall wrote: > > It's wrong to introduce a fundamental asymmetry that breaks the contract that > > an accessor can be used as a variable. > > 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. 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 <[EMAIL PROTECTED]> Senior Systems Engineer and Toolsmith "It's the sound of a satellite saying, 'get me down!'" -Shriekback
Re: A12: default accessors and encapsulation
On 2004-04-20 at 11:37:18, Larry Wall wrote: > So do whatever you like to the declarations, but make sure you preserve > the symmetry and extensibility of > > $obj.foo([EMAIL PROTECTED], *%NONSENSE) # get value of $.foo > $obj.foo([EMAIL PROTECTED], *%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 [EMAIL PROTECTED] and *%NONSENSE in > their getter and setter. 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
Re: A12: default accessors and encapsulation
On 4/20/04 2:37 PM, Larry Wall wrote: > On Tue, Apr 20, 2004 at 01:15:24PM -0400, John Siracusa wrote: > : 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 > > 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. 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
Re: A12: default accessors and encapsulation
On Tue, Apr 20, 2004 at 01:15:24PM -0400, John Siracusa wrote: : 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 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([EMAIL PROTECTED], *%NONSENSE) # get value of $.foo $obj.foo([EMAIL PROTECTED], *%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 [EMAIL PROTECTED] and *%NONSENSE in their getter and setter. Them's the specs. Larry
Re: A12: default accessors and encapsulation
On Tue, 20 Apr 2004, Luke Palmer wrote: > John Williams writes: > > On Tue, 20 Apr 2004, Luke Palmer wrote: > > > 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. > > > > 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 { ... } > > } > > Ugh! That's what I originally suggested, and it was shot down it was. > > My first solution to your problem introduced the traits C and > C, 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. 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
Re: A12: default accessors and encapsulation
On 4/20/04 12:14 PM, Luke Palmer wrote: > Okay, well, I thought that my example did that, but apparently using > C and C 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?) 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
Re: A12: default accessors and encapsulation
On 2004-04-20 at 10:51:47, Luke Palmer wrote: > I guess I bogged down that message with the implementation, so the > result may have been easy to miss. 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 | [EMAIL PROTECTED] Atlanta, GA 30348 USA | +1 404 827 4754
Re: A12: default accessors and encapsulation
John Williams writes: > On Tue, 20 Apr 2004, Luke Palmer wrote: > > 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. > > 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 { ... } > } Ugh! That's what I originally suggested, and it was shot down it was. My first solution to your problem introduced the traits C and C, 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
Re: A12: default accessors and encapsulation
On Tue, 20 Apr 2004, Luke Palmer wrote: > 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. 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
Re: A12: default accessors and encapsulation
Mark J. Reed writes: > Let me just chime in with my support for John's basic idea. I would > definitely prefer that it be easy to arrange things such that > > $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. Okay, well, I thought that my example did that, but apparently using C and C 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 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: 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
RE: A12: default accessors and encapsulation
> -Original Message- > From: Mark J. Reed [mailto:[EMAIL PROTECTED] > > Let me just chime in with my support for John's basic idea. I would > definitely prefer that it be easy to arrange things such that > > $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. 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 and/or C 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 logic for you, provided that you follow a simple xxx_ method naming convention. =Austin
Re: A12: default accessors and encapsulation
Mark J. Reed wrote: Let me just chime in with my support for John's basic idea. I would definitely prefer that it be easy to arrange things such that $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.
Re: A12: default accessors and encapsulation
Let me just chime in with my support for John's basic idea. I would definitely prefer that it be easy to arrange things such that $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
Re: A12: default accessors and encapsulation
On 4/20/04 1:25 AM, Luke Palmer wrote: > John Siracusa writes: >> 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. > > 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. 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
Re: A12: default accessors and encapsulation
On 4/19/04 10:04 PM, Damian Conway wrote: > John Siracusa wrote: >> 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 [...] So, presumably, by defining a new C, you > would be able to override the default accessor-generating behaviour. 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
Re: A12: default accessors and encapsulation
On 4/19/04 7:20 PM, Larry Wall wrote: > On Mon, Apr 19, 2004 at 06:53:29PM -0400, John Siracusa wrote: > : Yeah, that's exactly what I don't want to type over and over :) > > 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? :-) 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
Re: A12: default accessors and encapsulation
> On 4/19/04 3:58 PM, Austin Hastings wrote: > 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 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 [EMAIL PROTECTED]
Re: A12: default accessors and encapsulation
On Mon, Apr 19, 2004 at 06:53:29PM -0400, John Siracusa wrote: : Yeah, that's exactly what I don't want to type over and over :) 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
Re: A12: default accessors and encapsulation
John Siracusa wrote: > 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, 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/ ? /) ($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
Re: A12: default accessors and encapsulation
John Siracusa writes: > On 4/19/04 3:58 PM, Austin Hastings wrote: > >> 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? > > > > has $.name is rw > > will STORE { .set_name($^name); }; > > 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. 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 = // ?? 'male' :: 'female'; $.name = $in; }, ); } } Yuck. Much nicer: class Dog { has $.name; has $.gender; method name() will get { $.name } will set -> $in { $.gender = // ?? 'make' :: 'female'; $.name = $in; } { } } This is nothing new. So, for fun, here's the implementation of C and C: 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
Re: A12: default accessors and encapsulation
On 4/19/04 4:47 PM, [EMAIL PROTECTED] wrote: >> On 4/19/04 3:58 PM, Austin Hastings wrote: >> 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 > > 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;} 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
Re: A12: default accessors and encapsulation
On 4/19/04 3:58 PM, Austin Hastings wrote: >> 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? > > has $.name is rw > will STORE { .set_name($^name); }; 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
RE: A12: default accessors and encapsulation
> -Original Message- > From: John Siracusa [mailto:[EMAIL PROTECTED] > 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 { ... } ... } } } > > 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
Re: A12: default accessors and encapsulation
John Siracusa skribis 2004-04-19 14:20 (-0400): > has $.gender is rw; > (...) > 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? 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
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? 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