develooper Front page | perl.perl6.language | Postings from February 2004

Thinking about Accessors

Thread Next
John Williams
February 27, 2004 18:38
Thinking about Accessors
Message ID:
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

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

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 $ is no longer what it
looks like (a method with no arguments).  "$ += 3" is transformed
into "$$ + 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 $ 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

Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About