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

cod*_*ons 6 rakudo raku

这个问题与Apply a proxy using traits几乎重复。但是,这个问题涉及将代理应用于属性,我想对变量做同样的事情。从乔纳森的回答中,我明白我

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

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

(理想情况下,我想支持使用带有赋值语法的变量/特征。在一个完美的世界中,我的语法如下:

my $thing is custom-proxy = 42;
Run Code Online (Sandbox Code Playgroud)

其结果将是$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))
}
Run Code Online (Sandbox Code Playgroud)

这适用于未初始化为不同值的state变量或调用函数而不是第一个的变量。

Bra*_*ert 5

你可以随时绑定。

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;
Run Code Online (Sandbox Code Playgroud)

目前导致以下结果。

fetch
fetch
fetch
fetch
fetch
fetch
fetch
42
store 5
fetch
fetch
fetch
fetch
fetch
fetch
fetch
5
Run Code Online (Sandbox Code Playgroud)

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


如果你想要像这样的语法

my $thing is custom-proxy = 42;
Run Code Online (Sandbox Code Playgroud)

你需要从

multi trait_mod:<is> ( Variable:D \var, :$custom-proxy! ){
    …
}
Run Code Online (Sandbox Code Playgroud)

问题是目前这样做需要很多我不具备的深厚的 Rakudo/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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

我们如何$v.var.VAR从 aScalar变为 a Proxy
我不知道。

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


rai*_*iph 4

我的 2d [1]

我宁愿让它通过绑定工作:=

sub custom-proxy is rw { Proxy.new: FETCH => { 42 }, STORE => { ... } }
my $variable := custom-proxy;
say $variable; # 42
Run Code Online (Sandbox Code Playgroud)

在完美的世界中,我的语法如下:

my $thing is custom-proxy = 42;
Run Code Online (Sandbox Code Playgroud)

哎哟,这就是@Larry 的意图。

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

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

首先,让我们澄清一下 aVariable是什么,以及成功绑定到什么:

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

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

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

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

$a.set_build: -> \SELF, | {
  $a.set_value: SELF, Proxy.new:
    STORE => -> $, $val { say "store $val" },
    FETCH => { say "fetch!"; 42 }
}
Run Code Online (Sandbox Code Playgroud)

那么也许您可以使用这种方法来改变.vara 的属性Variable

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

因此,我认为这种技术在这种情况下不会有帮助,因为在将 传递给特征时,大概已经构建了Variable及其属性。.varVariableis

总之,虽然在编译时调用特征,但我认为调用得太晚,因为该var属性已经被永久绑定。


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

这可能是 @Larry 猜测is type有一天将实施对标量变量进行更多控制的猜测的背后原因。

脚注

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