使用特征应用代理

use*_*601 7 perl6 raku

我想为一个属性赋予特征,以为其提供代理,以便即使$!direct-access在一个类中也可以操纵所见内容的值。

这就是我现在所拥有的,但是正如您所看到的,从未调用过实际的get / set或store / fetch方法。我可能没有正确地应用它们,但是我发现的唯一示例似乎有大量额外的代码,它们的工作方式超出了我的需要,但我无法隔离重要的部分。

# The below shows on a Str more or less how I'd expect things to work
# although the %data wouldn't be hard coded of course

my Str $localized := do {
  my %data = hi => "hola", bye => "adiós";
  my $str  = "";
  Proxy.new:
    :STORE{ $str = @_[1] },
    :FETCH{ with %data{$str} { $_ } else { $str } }
}

$localized = "hi";
say $localized;
$localized = "bye";
say $localized;
$localized = "not defined";
say $localized;

# This seems to almost work, 

multi trait_mod:<is>(Attribute:D $a, :$localized!) {

  say $a.container.VAR.WHAT;

  $a.container.VAR does role Localized {
    has $!str;
    method STORE($a) { say "store!"; $!str = $a }
    method FETCH {say "fetch!"}
  }
}


class Foo {
  has Str $.text is localized;
}

my $foo = Foo.new;
say $foo.text, " <-- should be in Spanish";
$foo.text = "bye";
say $foo.text, " <-- should be in Spanish";
Run Code Online (Sandbox Code Playgroud)

尽管在这种情况下STORE正确地调用了该方法,但fetch方法却未正确调用,而is的要点$foo.textScalar+{Localized}.new,这表明我不太正确地应用东西。

Jon*_*ton 7

您需要安排Proxy将其绑定到属性中,以便存在一个类Proxy,而不是Scalar通常由类初始化逻辑创建的容器。这可以通过设置构建逻辑来实现(尽管如果采用这种方法,您将覆盖任何初始默认值),并Proxy在每次创建对象时使用该逻辑将属性绑定到新属性:

multi trait_mod:<is>(Attribute:D $a, :$localized!) {
    $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)

然后,这将调用FETCHSTORE回调(请注意,FETCH由于内部原因(例如类型检查以及您直接看到的那些访问),可能会调用它们:

class C {
    has $.attr is localized is rw;
}
my $c = C.new;
$c.attr = 'foo';
my $x = $c.attr;
Run Code Online (Sandbox Code Playgroud)

此示例说明了它也可以在类内部的属性读取中起作用:

class C {
    has $.attr is localized is rw;

    method m() {
        $!attr = 'foo';
        my $x = $!attr
    }
}
C.new.m;
Run Code Online (Sandbox Code Playgroud)