如何知道在使用 FALLBACK 时是否返回左值?

How to know if returning an l-value when using `FALLBACK`?

我如何知道在使用 FALLBACK 时我是否真的需要 return 左值?

我正在使用 return-rw,但我只想尽可能使用 return。我想跟踪我是否实际修改了 %!attrs 或仅在调用 FALLBACK 时读取了值。

或者(备用计划 B)我可以附加回调或类似于我的 %!attrs 的东西来监控变化吗?

class Foo {
  has %.attrs;
  submethod BUILD { %!attrs{'bar'} = 'bar' }

#  multi method FALLBACK(Str:D $name, *@rest) {
#    say 'read-only';
#    return %!attrs{$name} if %!attrs«$name»:exists;
#  }

  multi method FALLBACK(Str:D $name, *@rest) {
    say 'read-write';
    return-rw %!attrs{$name} if %!attrs«$name»:exists;
  }
}

my $foo = Foo.new;
say $foo.bar;

$foo.bar = 'baz';
say $foo.bar;

这感觉有点像 X-Y 问题,所以让我们简化示例,看看答案是否有助于您做出决定。

首先:如果您 return 散列中不存在的键的 "value",您实际上是在 returning 一个容器,它将自动激活分配给时哈希中的键:

my %hash;
sub get($key) { return-rw %hash{$key} }
get("foo") = 42;
dd %hash;   # Hash %hash = {:foo(42)}

请注意,您需要在此处使用 return-rw 以确保实际容器被 returned,而不仅仅是容器中的值。或者,您可以使用 is raw 特性,它允许您只设置最后一个值:

my %hash;
sub get($key) is raw { %hash{$key} }
get("foo") = 42;
dd %hash;   # Hash %hash = {:foo(42)}

请注意,在这种情况下,您应该使用return,因为它仍然会再次去容器化。

回到你的问题:

I want to track if I've actually modified %!attrs or have only just read the value when FALLBACK was called.

class Foo {
    has %!attrs;
    has %!unexpected;

    method TWEAK() { %!attrs<bar> = 'bar' }

    method FALLBACK(Str:D $name, *@rest) is raw {
        if %!attrs{$name}:exists {
            %!attrs{$name}
        }
        else {
            %!unexpected{$name}++;
            Any
        }
    }
}

这将 return 在散列中找到的容器,或者记录对未知密钥的访问和 return 一个不可变的 Any

关于计划 B,记录更改:为此您可以使用 Proxy 对象。

希望这对您的探索有所帮助。

Liz 的回答充满了有用的信息,你已经接受了,但我认为以下内容可能仍然有用。

How to know if returning an l-value ... ?

让我们从忽略 FALLBACK 子句开始。

您必须测试该值。要处理Scalars,必须测试值的.VAR。 (对于非 Scalar 值,.VAR 的行为类似于 "no op"。)我 认为 (但不要引用我的话)Scalar|Array|Hash 涵盖所有 l-value 超类型:

my \value       = 42;  # Int is an l-value is False
my \l-value-one = $;   # Scalar is an l-value is True
my \l-value-too = @;   # Array is an l-value is True

say "{.VAR.^name} is an l-value is {.VAR ~~ Scalar|Array|Hash}"
  for value, l-value-one, l-value-too

How to know if returning an l-value when using FALLBACK?

添加 "when using FALLBACK" 对答案没有影响。

How can I know if I actually need to return an l-value ... ?

同样,让我们​​从忽略 FALLBACK 子句开始。

这是一个与 "How to know if returning an l-value ... ?" 完全不同的问题。我认为这是你问题的核心。

Afaik,答案是,您需要预测 returned 值将如何使用。如果有 any 机会将它用作 l-value,并且您希望该用法有效,那么您需要 return 一个 l 值 。 language/compiler 不能(或至少不能)帮助您做出决定。

考虑一些相关场景:

my $baz := foo.bar;
... (100s of lines of code) ...
$baz = 42;

除非第一行 return 是 l 值 ,否则第二行将失败。

但实际情况比这更直接:

routine-foo = 42;

routine-foo 首先完整求值,lhs = rhs 表达式求值之前 .

除非编译器对 routine-foo 调用的解析以某种方式包含了以下事实,即接下来发生的事情将是 lhs 将被分配给,否则将无法单独或多次分派 routine-foo 以了解它是否可以安全地 return 一个 r 值 或必须 return 一个 l 值.

并且编译器的解析包含它。因此,例如:

multi term:<bar> is rw { ... }
multi term:<bar>       { ... }
bar = 99; # Ambiguous call to 'term:<bar>(...)'

我可以想象这一天(N年后)通过允许=成为可重载运算符、允许重载的健壮宏的组合来解决= 可用,并且例程解析被修改,因此上述不明确的调用可以做一些等同于解析到 is rw multi 的事情。但我怀疑即使 N=10,它 实际上 也会通过。也许还有另一种方法,但我暂时想不出。

How can I know if I actually need to return an l-value when using FALLBACK?

同样,添加 "when using FALLBACK" 对答案没有影响。

I want to track if I've actually modified %!attrs or have only just read the value when FALLBACK was called.

当调用 FALLBACK 时,它不知道在什么上下文中调用它 -- r-valuel-value。在已经 returned.

之后进行任何修改

换句话说,无论你想出什么解决方案本身都与 FALLBACK 无关(即使你必须使用它来实现你正在尝试的任何其他方面)做)。

(即使是,我怀疑尝试通过 FALLBACK 本身来解决它只会让事情变得更糟 。可以想象写两个 FALLBACK multis,一个具有 is rw 特征的,但是,如上所述,我的想象力并没有延伸到很快就会产生任何变化,如果有的话,并且只有在上述想象的事情发生时才会发生(宏等) .) and 编译器被 also 修改以注意两个 FALLBACK multi 变体,我根本不是这个意思暗示这甚至是有道理的。)

B计划

Or (alternate plan B) can I attach a callback or something similar to my %!attrs to monitor for changes?

正如 Lizmat 所说,那是 Proxy 的领域。因此你的下一个 SO 问题...... :)