On Fri, Feb 27, 2004 at 09:27:20PM -0700, John Williams wrote: : On Fri, 27 Feb 2004, Luke Palmer wrote: : > John Williams writes: : > > I want to get from here : > > : > > method bar_attr(?$val) is accessor { : > > $.bar_attr = $val if exists $val; : > > return $.bar_attr; : > > } : > > : > I think this is a good idea. Proxies are a little advanced for someone : > who, say, comes from C# and doesn't understand tying yet. I think : > you're going about it wrong, though. Here's how I imagine this working: : > : > method bar_attr() will access -> $self: ?$val { : > $.bar_attr = $val if exists $val; : > return $.bar_attr; : > }
All the solutions with an optional argument are retro (in the uncool sense) insofar as it's relying on conditionals where you should be relying on dispatch. : Well, I like being able to just say "is accessor" better... For the default case you don't even need that. You just declare the attribute: has $.bar_attr is rw; and you get a rw accessor automatically. It's only if you want to wrap some semantics that you have to get fancier. : > Or, alternatively: : > : > method bar_attr() will get { $.bar_attr } : > will set { $.bar_attr = $_ } : : ... but this is good. I'd forgotten about C<do>. Yes, that is a much clearer and cleaner approach, except to the compiler, which would still be looking for a C<will do> property there at the end... : > And the traits C<access>, C<get>, and C<set> define C<do> to do the : > right thing. The only trickyness that I see is getting C<get> and : > C<set> to work together, since they each need to define part of the same : > proxy class. : : It's just setting the FETCH and STORE attributes on a shared Proxy : class, if I understood that right. : Maybe the traits put a Proxy property on the method they are applied to, : and all modify that same Proxy property. Not good enough, unless the property generates proxies. : Then, if the get and set traits are just mixed-in with the Method class, : $self for the trait would refer to the Method itself, so they can just : define C<do> to return the Method's Proxy property. : : # create the Proxy property : $self but= Proxy.new(); : : # add the parts defined by traits : $self.Proxy.STORE = $self.set if exists $self.set; : $self.Proxy.FETCH = $self.get if exists $self.get; : : # C<do> is always the same : $self.do = { $self.Proxy }; : : Even simpler than wrapping! But still wrong... : Or maybe C<get> and C<set> are parts of the Accessor trait, so you need to : apply it to use them. : : method bar_attr() is accessor : will get { $.bar_attr } : will set { $.bar_attr = $_ }; : : method bar_attr(?$val) is accessor { : $.bar_attr = $val if exists $val; : return $.bar_attr; : } And that's still wronger... : And we can have it both ways: : : trait accessor { : [hand-waving declaration for the trait APPLY method] { : $self but= Proxy.new(); : : if (exists $self.do) { : $self.Proxy.STORE = { $self.do($^a) }; : $self.Proxy.FETCH = { $self.do() }; : } else { : $self.Proxy.STORE = $self.set // { die "read-only!" }; : $self.Proxy.FETCH = $self.get // { die "write-only!" }; : } : : $self.do = { $self.Proxy }; : } : } : : Am I off the deep end yet? Way. :-) : > > trait accessor { : > > my $proxy is Proxy; : > > : > > method APPLY( Code $acc : ) { : > : > I don't think that's right. : : I'm certain it's wrong. But I had to put something there. It could also : be that the trait is constructed at the same time as the object it applies : to, and its CREATE method gets called with the same arguments. Waiting : for A12... Hmm, you've just managed to delay A12 by asking me to explain something I haven't figured out yet... :-) : > > # "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; : > : > C<rw> is a trait, not a role. So you can't C<but> it on. Probably. : > : > > # wrap the applyee : > > $acc.wrap( sub (?$val) { : > > $proxy = $val if exists $val; # so $foo.bar(1) still works : > > return $proxy; : > > } ); : > > } : > > } : > : > That looks okay to me, little as I know about traits. I'm a little : > uneasy about declaring the Proxy as a lexical in trait scope; I'd be : > inclined to declare it as a lexical in the C<APPLY> routine, or even : > in the anonymous sub that you're wrapping with. : : I had it in the wrapper sub in the first version, but I didn't like that : it was recreating the Proxy object every time someone accessed the : accessor. So I moved it to where I only needed to create it once. And that's the nub of the problem--you can't return the same object every time, because it has to act like a variable, including being able to take references to it, temporize it, and such. If it were just for attributes, you might get by with one static proxy per attribute per object. However, in the general case, the proxy also has to function as a closure over the arguments to the method, which could vary from call to call. Larry