使用`FALLBACK`时如何知道是不是返回左值?

Posted

技术标签:

【中文标题】使用`FALLBACK`时如何知道是不是返回左值?【英文标题】:How to know if returning an l-value when using `FALLBACK`?使用`FALLBACK`时如何知道是否返回左值? 【发布时间】:2019-09-24 01:05:59 【问题描述】:

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

我正在使用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;

【问题讨论】:

【参考方案1】:

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

首先:如果您在哈希中返回一个不存在的键的“值”,您实际上是在返回一个容器,该容器将在分配给时自动激活哈希中的键:

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

请注意,您需要在这里使用return-rw 来确保返回实际的容器,而不仅仅是容器中的值。或者,您可以使用 is raw 特征,它允许您设置最后一个值:

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

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

回到你的问题:

我想跟踪我是否真的修改了%!attrs,或者只是在调用FALLBACK 时读取了值。

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
        
    

这将返回在哈希中找到的容器,或者记录对未知密钥的访问并返回不可变的Any

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

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

【讨论】:

感谢您的洞察力。我真的很感激:)【参考方案2】:

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

如何知道是否返回左值...?

让我们先忽略FALLBACK 子句。

您必须测试该值。处理Scalars,必须测试.VAR的值。 (对于非Scalar 值,.VAR 就像“无操作”。)我认为(但不要引用我的话)Scalar|Array|Hash 涵盖了所有 l-值 超类型:

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

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

添加“使用FALLBACK时”对答案没有影响。

我怎么知道我是否真的需要返回一个l-value ...?

再次,让我们先忽略FALLBACK 子句。

这是一个与“如何知道是否返回左值...?”完全不同的问题。我认为这是您问题的核心。

Afaik,答案是,您需要预测返回值将如何使用。如果有任何机会将其用作l-value,并且您希望该用法起作用,那么您需要返回一个l-value。语言/编译器不能(或至少不能)帮助您做出决定。

考虑一些相关场景:

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

除非第一行返回一个l-value,否则第二行将失败。

但情况实际上比这更直接:

routine-foo = 42;

首先评估routine-foo在评估lhs = rhs 表达式之前

除非编译器对 routine-foo 调用的解析以某种方式结合了这样一个事实,即接下来发生的事情就是将分配给 lhs,否则将无法单独或多次调度 @987654338 @ 知道它是否可以安全地返回一个 r-value 或必须返回一个 l-value

而编译器的分辨率并没有 包含这一点。因此,例如:

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

我可以想象这一天(从现在起 N 年后)通过允许 = 成为可重载运算符、允许重载 = 可用的健壮宏的组合来解决,并修改了例程解析,因此上述模棱两可的调用可以做相当于解析is rw multi的事情。但我怀疑即使 N=10,它实际上也会通过。也许还有另一种方法,但我现在想不出。

在使用FALLBACK时,我如何知道我是否真的需要返回一个左值

同样,添加“使用FALLBACK 时”对答案没有影响。

我想跟踪我是否真的修改了%!attrs,或者只是在调用FALLBACK 时读取了值。

FALLBACK 被调用时,它不知道它在什么上下文中被调用 -- r-valuel-value。任何修改都是在它已经返回之后进行的。

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

(即使是这样,我怀疑尝试通过 FALLBACK 本身解决它只会使问题更糟。可以想象编写两个 FALLBACK multis,一个具有 is rw trait ,但是,如上所述,我的想象力并没有延伸到任何时候,如果有的话,很快就会产生任何影响,并且只有当上述想象的事情发生(宏等)时才会发生编译器被修改以注意两个FALLBACK multi 变体,我完全没有意思暗示这甚至是有道理的。)

B计划

或者(备选方案 B)我可以附加回调或类似于我的%!attrs 的内容来监控更改吗?

正如 Lizmat 所说,这是Proxys 的领域。因此,您的下一个 SO 问题... :)

【讨论】:

是的,在发现 Proxy 无法处理哈希之前,我很兴奋并接受了上述答案。我一直在试验,看看我可以在 Ruby 中做的元编程东西是否可以在 Perl 6 中实现。如果其中一些还没有,我可以等待。 ¯\_(ツ)_/¯

以上是关于使用`FALLBACK`时如何知道是不是返回左值?的主要内容,如果未能解决你的问题,请参考以下文章

从函数返回的左值引用实际上是右值吗(从调用者的角度来看)?

SpringCloud-Hystrix-如何使用Fallback

当 Route::fallback 存在时检查路由是不是存在

应用间接时,标准是不是要求指针变量的左值到右值转换?

为啥数组不是左值? [复制]

C语言 啥叫做左值?右值?