使用`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
子句。
您必须测试该值。处理Scalar
s,必须测试.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-value 或 l-value。任何修改都是在它已经返回之后进行的。
换句话说,无论你想出什么解决方案本身都与FALLBACK
无关(即使你必须使用它来实现你正在尝试做的任何其他方面)。
(即使是这样,我怀疑尝试通过 FALLBACK
本身解决它只会使问题更糟。可以想象编写两个 FALLBACK
multis,一个具有 is rw
trait ,但是,如上所述,我的想象力并没有延伸到任何时候,如果有的话,很快就会产生任何影响,并且只有当上述想象的事情发生(宏等)和时才会发生编译器也被修改以注意两个FALLBACK
multi 变体,我完全没有意思暗示这甚至是有道理的。)
B计划
或者(备选方案 B)我可以附加回调或类似于我的
%!attrs
的内容来监控更改吗?
正如 Lizmat 所说,这是Proxy
s 的领域。因此,您的下一个 SO 问题... :)
【讨论】:
是的,在发现 Proxy 无法处理哈希之前,我很兴奋并接受了上述答案。我一直在试验,看看我可以在 Ruby 中做的元编程东西是否可以在 Perl 6 中实现。如果其中一些还没有,我可以等待。¯\_(ツ)_/¯
以上是关于使用`FALLBACK`时如何知道是不是返回左值?的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud-Hystrix-如何使用Fallback