I'm trying to find how the binding operation works on attributes and what makes it so different from nqp::bindattr
. Consider the following example:
class Foo {
has @!foo;
submethod TWEAK {
my $fval = [<a b c>];
use nqp;
nqp::bindattr( nqp::decont(self), $?CLASS, '@!foo',
#@!foo :=
Proxy.new(
FETCH => -> $ { $fval },
STORE => -> $, $v { $fval = $v }
)
);
}
method check {
say @!foo.perl;
}
}
my $inst = Foo.new;
$inst.check;
It prints:
$["a", "b", "c"]
Replacing nqp::bindattr
with the binding operator from the comment gives correct output:
["a", "b", "c"]
Similarly, if foo
is a public attribute and accessor is used the output would be correct too due to deconterisation taking place within the accessor.
I use similar code in my AttrX::Mooish
module where use of :=
would overcomplicate the implementation. So far, nqp::bindattr
did the good job for me until the above problem arised.
I tried tracing down Rakudo's internals looking for :=
implementation but without any success so far. I would ask here either for an advise as to how to simulate the operator or where in the source to look for its implementation.
Before I dig into the answer: most things in this post are implementation-defined, and the implementation is free to define them differently in the future.
To find out what something (naively) compiles into under Rakudo Perl 6, use the
--target=ast
option (perl6 --target=ast foo.p6
). For example, the bind in:Comes out as:
While switching it for
@!a
like here:Comes out as:
The
decont
instruction is the big difference here, and it will take the contents of theProxy
by calling itsFETCH
, thus why the containerization is gone. Thus, you can replicate the behavior by insertingnqp::decont
around theProxy
, although that rather begs the question of what theProxy
is doing there if the correct answer is obtained without it!Both
:=
and=
are compiled using case analysis (namely, by looking at what is on the left hand side).:=
only works for a limited range of simple expressions on the left; it is a decidedly low-level operator. By contrast,=
falls back to asub
call if the case analysis doesn't come up with a more efficient form to emit, though in most cases it manages something better.The case analysis for
:=
inserts adecont
when the target is a lexical or attribute with sigil@
or%
, since - at a Perl 6 level - having an item bound to an@
or%
makes no sense. Usingnqp::bindattr
is going a level below Perl 6 semantics, and so it's possible to end up with theProxy
bound directly there using that. However, it also violates expectations elsewhere. Don't expect that to go well (but it seems you don't want to do that anyway.)