使用特征将代理应用于变量(而不是属性)



这个问题几乎是使用特征应用代理的重复。 但是,该问题涉及将代理应用于属性,我想对变量做同样的事情。 从乔纳森的回答中,我明白了我

需要安排将代理绑定到属性中,以便那里有一个代理,而不是通常由类初始化逻辑创建的标量容器。

但是,即使在编译时,我似乎也无法成功绑定到 Variable:D。(包括使用 nqp::bind)。 我将非常感谢任何正确方向的指示。

(理想情况下,我想支持将变量/特征与赋值语法一起使用。 在一个完美的世界里,我会有这样的语法:

my $thing is custom-proxy = 42;

这样做的结果是$thing在代理内部容器化,而不是在标量中。但如果这是不可能的,我会满足于让它通过:=绑定工作。

[编辑:基于下面公认的答案,可以主要使用以下代码来做到这一点:


multi trait_mod:<is>(Variable v, :$tom) {
v.block.add_phaser(
'ENTER',
v.willdo(<-> $_ {
$_ = Proxy.new:
STORE => -> $, $v { say "store $v" },
FETCH => { say "fetch!"; 42}
}, 1))
}

这适用于未初始化为不同值的变量,或者用于调用第一个函数以外的函数时state变量。

您可以随时绑定。

my $actual-thing = 42;
my $thing := Proxy.new(
FETCH => anon method fetch () {
say 'fetch';
$actual-thing
},
STORE => anon method store ($new) {
say 'store ',$new;
$actual-thing = $new
}
);
say $thing;
$thing = 5;
say $thing;

目前导致以下结果。

fetch
fetch
fetch
fetch
fetch
fetch
fetch
42
store 5
fetch
fetch
fetch
fetch
fetch
fetch
fetch
5

(重复的FETCH调用是已知限制。


如果你想有这样的语法

my $thing is custom-proxy = 42;

您需要从

multi trait_mod:<is> ( Variable:D var, :$custom-proxy! ){
…
}

问题是,目前以这种方式这样做需要很多我不具备的深厚的乐道/nqp 知识。

例如,my $var is default('value')背后的代码看起来有点像这样:

multi sub trait_mod:<is>(Variable:D $v, Mu :$default!) {
my $var  := $v.var;
my $what := $var.VAR.WHAT;
my $descriptor;
{
$descriptor := nqp::getattr($var, $what.^mixin_base, '$!descriptor');
CATCH {
my $native = $v.native($what);
…
}
}
…
$descriptor.set_default(nqp::decont($default));
# make sure we start with the default if a scalar
$var = $default if nqp::istype($what, Scalar);
}

为什么这有$what.^mixin_base
我不知道。

为什么$!descriptor无法访问类似$v.var.descriptor
我不知道的东西。

我们如何将$v.var.VARScalar更改为Proxy
我不知道。

最后一个可行吗?(从trait_mod:<is>内) 我相当肯定答案是肯定的。

My 2d[1]

我会满足于让它通过:=绑定工作。

sub custom-proxy is rw { Proxy.new: FETCH => { 42 }, STORE => { ... } }
my $variable := custom-proxy;
say $variable; # 42

在一个完美的世界里,我会有这样的语法:

my $thing is custom-proxy = 42;

阿依,这就是@Larry的意图。

但是,正如您可能知道的那样,如果使用is特征将类型(例如role custom-proxy { ... })应用于标量变量(例如my $variable is custom-proxy),则编译器会发出编译时错误消息(is trait on $-sigil variable not yet implemented)。

即使在编译时,我似乎也无法成功绑定到Variable:D

首先,让我们阐明什么是Variable,以及成功绑定到什么需要:

multi trait_mod:<is>(Variable var, :$foo!) { say var.var.VAR.WHAT } # (Scalar)
my $variable is foo;

您可能认为可以绑定到var。但是编译器传递了一个左值,因此您将无法更改它。

您可能认为可以绑定到var.var,这是Variable的属性。(我解释了什么是Variable,以及它的var属性,以及为什么我必须在上面的代码中写"varvarVAR!"。

您链接的 SO 显示了如何更改绑定到某个对象中的属性的值:

$a.set_build: -> SELF, | {
$a.set_value: SELF, Proxy.new:
STORE => -> $, $val { say "store $val" },
FETCH => { say "fetch!"; 42 }
}

因此,也许您可以使用这种方法来更改Variable.var属性?

不幸的是,"设置构建逻辑"用于"绑定属性......在每次创建对象时",(因此"您将覆盖任何初始默认值")。

因此,我认为这种技术在这种情况下不会有所帮助,因为Variable,因此它的.var属性,在Variable传递给is特征时可能已经构建好了。

总之,虽然在编译时调用了一个 trait,但我认为它被调用得太晚了,因为var属性已经被永久绑定了。


我的猜测是,改变 Raku(do) 以使Variable.var属性变得可写,或者使用元编程潜入Variable的公共 API 之下以强制进行更改,将不合理地使编译器的变量处理代码复杂化和/或将代码生成优化逻辑替换为悲观逻辑。

这可能是@Larry猜测有朝一日将实施对标量变量进行更可控is type的原因。

脚注

[1]我的两个(便士|狗狗币)。

最新更新