我试图找到对属性进行绑定操作的方式以及使其与nqp::bindattr
如此不同的原因。考虑以下示例:
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;
它打印:
$["a", "b", "c"]
用注释中的绑定运算符替换nqp::bindattr
可以得到正确的输出:
["a", "b", "c"]
类似地,如果foo
是一个公共属性,并且使用了访问器,则由于访问器中发生的去确定性操作,输出也将是正确的。
我在AttrX::Mooish
模块中使用了类似的代码,其中使用:=
会使实现变得过于复杂。到目前为止,nqp::bindattr
对我一直很好,直到出现上述问题。
我尝试跟踪Rakudo的内部结构,以寻求实现:=
的方法,但到目前为止没有任何成功。我想问一下关于如何模拟运算符或在源代码中何处寻找其实现的建议。
答案 0 :(得分:10)
在深入探讨答案之前:这篇文章中的大多数内容都是实现定义的,并且实现可以自由定义将来的内容。
要找出Rakudo Perl 6下(天真的)可以编译的内容,请使用--target=ast
选项(perl6 --target=ast foo.p6
)。例如,绑定在:
class C {
has $!a;
submethod BUILD() {
my $x = [1,2,3];
$!a := $x
}
}
结果为:
- QAST::Op(bind) :statement_id<7>
- QAST::Var(attribute $!a) <wanted> $!a
- QAST::Var(lexical self)
- QAST::WVal(C)
- QAST::Var(lexical $x) $x
在为@!a
切换时,如下所示:
class C {
has @!a;
submethod BUILD() {
my $x = [1,2,3];
@!a := $x
}
}
结果为:
- QAST::Op(bind) :statement_id<7>
- QAST::Var(attribute @!a) <wanted> @!a
- QAST::Var(lexical self)
- QAST::WVal(C)
- QAST::Op(p6bindassert)
- QAST::Op(decont)
- QAST::Var(lexical $x) $x
- QAST::WVal(Positional)
decont
指令在这里有很大的不同,它将通过调用Proxy
来获取FETCH
的内容,因此也就没有了容器化的原因。因此,您可以通过在nqp::decont
周围插入Proxy
来复制行为,尽管这样会产生一个问题,即如果没有答案,Proxy
会在这里做什么! >
:=
和=
都是使用案例分析(即,通过查看左侧的内容)进行编译的。 :=
仅适用于左侧有限范围内的简单表达式;它绝对是底层操作员。相比之下,如果案例分析没有提出更有效的形式来发送,则=
会回退到sub
调用,尽管在大多数情况下它可以管理得更好。
当目标是带有符号:=
或decont
的词汇或属性时,@
的案例分析会插入%
,因为-在Perl 6级别上-具有绑定到@
或%
的项目没有任何意义。使用nqp::bindattr
比Perl 6语义要低一些,因此可能以Proxy
结束直接绑定到那里。但是,它也违反了其他地方的期望。不要指望一切顺利(但似乎您还是不想这样做。)