Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Thinking about Accessors
There are currently too many topics in this group that display first. To make this topic appear first, remove this option from another topic.
There was an error processing your request. Please try again.
flag
  6 messages - Collapse all  -  Translate all to Translated (View all originals)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
John Williams  
View profile  
 More options Feb 27 2004, 9:48 pm
Newsgroups: perl.perl6.language
From: willi...@tni.com (John Williams)
Date: Fri, 27 Feb 2004 19:37:57 -0700 (MST)
Local: Fri, Feb 27 2004 9:37 pm
Subject: Thinking about Accessors
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


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Luke Palmer  
View profile  
 More options Feb 27 2004, 10:00 pm
Newsgroups: perl.perl6.language
From: fibon...@babylonia.flatirons.org (Luke Palmer)
Date: Fri, 27 Feb 2004 19:57:05 -0700
Local: Fri, Feb 27 2004 9:57 pm
Subject: Re: Thinking about Accessors

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


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
John Williams  
View profile  
 More options Feb 27 2004, 11:48 pm
Newsgroups: perl.perl6.language
From: willi...@tni.com (John Williams)
Date: Fri, 27 Feb 2004 21:27:20 -0700 (MST)
Local: Fri, Feb 27 2004 11:27 pm
Subject: Re: Thinking about Accessors

Well, I like being able to just say "is accessor" better...

> Or, alternatively:

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

... but this is good.   I'd forgotten about C<do>.

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

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!

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

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

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.

~ John Williams


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Larry Wall  
View profile  
 More options Feb 28 2004, 1:00 pm
Newsgroups: perl.perl6.language
From: la...@wall.org (Larry Wall)
Date: Sat, 28 Feb 2004 09:51:01 -0800
Local: Sat, Feb 28 2004 12:51 pm
Subject: Re: Thinking about Accessors
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


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Aaron Sherman  
View profile  
 More options Feb 29 2004, 1:48 pm
Newsgroups: perl.perl6.language
From: a...@ajs.com (Aaron Sherman)
Date: Sun, 29 Feb 2004 13:44:47 -0500
Local: Sun, Feb 29 2004 1:44 pm
Subject: Re: Thinking about Accessors

On Fri, 2004-02-27 at 21:57, Luke Palmer wrote:
>     method bar_attr() will get { $.bar_attr }
>                       will set { $.bar_attr = $_ }

I'm confused by this in only one way... since method bar_attr and the
accessor bar_attr have the same name, how do I write an accessor that
recurses? Does C<.bar_attr> call my method, and not the accessor, or is
there some other way to do that?

--
Aaron Sherman <a...@ajs.com>
Senior Systems Engineer and Toolsmith
"It's the sound of a satellite saying, 'get me down!'" -Shriekback

  signature.asc
< 1K Download

 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
Larry Wall  
View profile  
 More options Feb 29 2004, 5:48 pm
Newsgroups: perl.perl6.language
From: la...@wall.org (Larry Wall)
Date: Sun, 29 Feb 2004 14:37:59 -0800
Local: Sun, Feb 29 2004 5:37 pm
Subject: Re: Thinking about Accessors
On Sun, Feb 29, 2004 at 01:44:47PM -0500, Aaron Sherman wrote:

: On Fri, 2004-02-27 at 21:57, Luke Palmer wrote:
:
: >     method bar_attr() will get { $.bar_attr }
: >                       will set { $.bar_attr = $_ }
:
: I'm confused by this in only one way... since method bar_attr and the
: accessor bar_attr have the same name, how do I write an accessor that
: recurses? Does C<.bar_attr> call my method, and not the accessor, or is
: there some other way to do that?

Within the class you can always get at the attribute via $.bar_attr.
If you call the accessor, it's always virtual, which means it might
be calling out to a derived class's wrapper of the same name.  That
method might end up deferring back to your method, of course.  Or did
you mean something else by "recurse"?

Larry


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.
End of messages
« Back to Discussions « Newer topic     Older topic »