Magic mutators and my $var is Proxy( ... );

22 views
Skip to first unread message

Sam Vilain

unread,
Jun 26, 2005, 4:35:21 AM6/26/05
to Perl 6 Language
To me it is a trivial case that you want to provide a fake attribute
which for all intents and purposes behaves exactly like there was a real
attribute there, backing against another attribute.

A Date object is a classic example of this; you want to provide 0-based
and 1-based attributes, which you want to present as equivalent to each
other.

So, we've got this "my $var is Proxy( ... )" construct in A06.
Say you've got this class:

class MagicVal {
has Int $.varies is rw;

method varies returns Int is rw {
return my $var is Proxy ( :for($.varies),
:FETCH{ $.varies += 2 },
:STORE{ $.varies = $_ + 1 },
);
}
}

Firstly, would this be correct syntax? In particular, what should
I call $.varies inside the :FETCH and :STORE subs? Would they close
over $?SELF, too?

If they did, doesn't this mean that $var will be a new Proxy attribute
every time that the attribute is read, and will have side effects?

Such as;

my $foo = MagicVal.new();
$foo.varies = 5; # sets to 6;
say $foo.varies; # says 8
say $foo.varies; # says 10
my $var = $foo.varies; # $var is proxy for attribute
say $foo.varies; # says 12
say $var; # says 14

It seems to me that the only time I'd want to actually return a
Proxy object is when taking a reference to the fake attribute. In
all other situations, you'd simply want to dispatch to either an
accessor or a mutator, depending on the context.

ie, I'd like the design of this feature to be sufficient that most
of the time, the Proxy object need never be constructed, and instead
the relevant closure could be bound to that method instead.

In particular, I think this would mean making Proxy objects
automatically call FETCH when returned, unless in reference context.

In fact, I think I'd rather see the proxy as a closure trait;

class MagicVal {
has Int $.varies is rw;

method varies returns Int is Proxy( :FETCH(&get_varies),
:STORE(&set_varies) );

method get_varies { $.varies += 2 };
method set_varies { $.varies = $^v + 1 };
}

Of course this wouldn't preclude the possibility of using the syntax
in A06 where it is more suited to the problem at hand. Also, in the
case where you do take a reference of a $foo.varies, then a Proxy object
could be constructed to DTRT based on the information there.

This would make the "is Proxy()" construct effectively a compile-time
want() switch.

Any opinions on this?

Sam.

Sam Vilain

unread,
Jun 26, 2005, 6:43:12 PM6/26/05
to Perl 6 Language
Sam Vilain wrote:
> To me it is a trivial case that you want to provide a fake attribute
> which for all intents and purposes behaves exactly like there was a real
> attribute there, backing against another attribute.
>
> A Date object is a classic example of this; you want to provide 0-based
> and 1-based attributes, which you want to present as equivalent to each
> other.

FWIW, I think this question also applies to fetching members from a hash
collection.

For example, if you have a function;

sub foo($foo) { $foo || "bar" }

And you call it, passing it a hash slot;

foo(%hash<key>);

Then it will be auto-vivified, as in Perl 5; because a reference to the
slot had to be taken. Actually there is no reference taken; it's a
read-only binding of the hash slot. But either way, it is a read-only
point in the signature that is affecting the input argument, which is
arguably (right^wrong).

By returning a Proxy object 'tied' to the slot, then this autovivification
could be circumvented and the straightforward implementation - passing
bound variables as transparent references, continues to be acceptable.
Albeit requiring that the Hash::postcircumfix:<{ }> method is a Proxy
method, too.

Sam.

Luke Palmer

unread,
Jun 27, 2005, 1:30:03 AM6/27/05
to Sam Vilain, Perl 6 Language
On 6/26/05, Sam Vilain <s...@vilain.net> wrote:
> So, we've got this "my $var is Proxy( ... )" construct in A06.
> Say you've got this class:
>
> class MagicVal {
> has Int $.varies is rw;
>
> method varies returns Int is rw {
> return my $var is Proxy ( :for($.varies),
> :FETCH{ $.varies += 2 },
> :STORE{ $.varies = $_ + 1 },
> );
> }
> }
>
> Firstly, would this be correct syntax? In particular, what should
> I call $.varies inside the :FETCH and :STORE subs? Would they close
> over $?SELF, too?

Yes, just like when you say `my $self = shift` in Perl 5. $self is closed over.

> If they did, doesn't this mean that $var will be a new Proxy attribute
> every time that the attribute is read, and will have side effects?

Well, your implementation has side-effects whether or not it's a new
Proxy every time.

> Such as;
>
> my $foo = MagicVal.new();
> $foo.varies = 5; # sets to 6;
> say $foo.varies; # says 8
> say $foo.varies; # says 10

Yep.

> my $var = $foo.varies; # $var is proxy for attribute
> say $foo.varies; # says 12
> say $var; # says 14

Nope. The `is Proxy` is a trait on the *container*. The container
does not pass through scalar assignment. If you bound using :=
instead in the first line of this quote, then the semantics you
describe would occur.



> It seems to me that the only time I'd want to actually return a
> Proxy object is when taking a reference to the fake attribute. In
> all other situations, you'd simply want to dispatch to either an
> accessor or a mutator, depending on the context.

That is precisely what will happen. It falls out of the semantics of
tied containers.

> ie, I'd like the design of this feature to be sufficient that most
> of the time, the Proxy object need never be constructed, and instead
> the relevant closure could be bound to that method instead.

I expect that it might be possible to optimize away such a proxy
object in non-referential context. The sub checks its context: if it
is lvalue, then it immediately calls the STORE method. If it is
rvalue, it immediately calls the FETCH method. However, as far as I
can tell, this optimization requires that Perl know about the Proxy
class in particular.



> In particular, I think this would mean making Proxy objects
> automatically call FETCH when returned, unless in reference context.
>
> In fact, I think I'd rather see the proxy as a closure trait;
>
> class MagicVal {
> has Int $.varies is rw;
>
> method varies returns Int is Proxy( :FETCH(&get_varies),
> :STORE(&set_varies) );

But other than that it looks good. I would name the trait something
other than Proxy probably. Perhaps `accessor`.

Luke

Reply all
Reply to author
Forward
0 new messages