John Williams writes:
> 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 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;
    }

Or, alternatively:

    method bar_attr() will get { $.bar_attr }
                      will set { $.bar_attr = $_ }

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.

> 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 : ) {

I don't think that's right.  That means, to apply the trait, you call
this way:

    &bar_attr.accessor::APPLY;

Where I would think it would be something more like:

    accessor.APPLY(&bar_attr);

Implying a signature like:

    method APPLY(Code $acc is rw) {...}

>             # "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.

Luke

> 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
> 
> 

Reply via email to