禁止无符号变量重新绑定是否有目的或好处?

Is there a purpose or benefit in prohibiting sigilless variables from rebinding?

在试图更好地理解无符号变量以及它们与 $ 有符号变量的区别时,我发现与 $ 有符号变量不同,无符号变量在初始化后无法重新启动:

my $a = 42;
my $b := $a;
$b := 42;       # No exception generated

my \c := $a;
c := 42;        # OUTPUT: «Cannot use bind operator with this left-hand side␤»

这是设计使然吗?如果是这样,在不禁止 $ 带符号变量的情况下,禁止无符号变量重新绑定是否有目的或好处?

无符号变量具有 Static Single Assignment 语义。

优化机会收益

引用自the Benefits section of the above linked SSA page

The primary usefulness of SSA comes from how it simultaneously simplifies and improves the results of a variety of compiler optimizations, by simplifying the properties of variables. For example, consider this piece of code:

y := 1
y := 2
x := y

Humans can see that the first assignment is not necessary, and that the value of y being used in the third line comes from the second assignment of y. A program would have to perform reaching definition analysis to determine this. But if the program is in SSA form, both of these are immediate:

y1 := 1
y2 := 2
x1 := y2

Compiler optimization algorithms which are either enabled or strongly enhanced by the use of SSA include [a long list of available optimizations].

因此,原则上,通过使用无符号变量编写代码,您可以为编译器提供更多优化机会。

人类推理的好处

一般来说,在 P6 中,变化的事物有一个印记,反之亦然,不变化的事物有一个印记,反之亦然。从这个角度来看,禁止重新绑定是有意义的。

(出于这个原因,我倾向于认为将无符号变量绑定到容器,正如我在对你的问题的评论中所展示的那样,尤其是绑定到 Scalar 容器,是一种可疑的做法。)

是的,这当然是设计使然,而且——就像 Perl (6) 设计中的大多数东西一样——出于不止一个原因。

在讨论无印记符号语法之前,值得花点时间回顾一下印记在语言中扮演的一些角色。其中包括:

  • 句法消歧(你可以随便调用一个变量,即使碰巧有一个关键字就是那个名字)
  • 可读性(由于印记,程序中的数据很突出)
  • 定义赋值语义(在 Perl 6 中赋值意味着 "copy in to",因此 my @a = @b 意味着迭代 @b 并将其中的每个东西放入 @a,因此任何未来的赋值@b 不会影响 @a)
  • 限制那里可以绑定的内容(例如,只有 Positional 的内容到 @
  • $ 的情况下,控制什么将被视为单个项目
  • 在签名参数 @ 的情况下,导致传入的 Seq 被缓存

因此,每个印记都带有一组有用的数据默认行为,这些数据被视为单个项目($),一些要按位置索引的东西(@),一些东西使用键 (%) 和可以调用的内容 (&) 进行索引。在赋值、低级绑定和签名绑定的上下文中,每一个都带有通常需要的那种数据语义。

这很好,很好,直到有人想编写对所有这些行为具有多态性的代码,或者不承诺任何印记行为。 Perl 6 语言设计的早期迭代有类似 sub foo($x is parcel) { } 的东西,其中 is parcel 实际上意味着 "don't impose any kind of sigil-y semantics on this",只是相当混乱,因为它有 $ 印记但被选择退出语义。人们意识到,如果不应用 sigil 行为,那么看起来不同会更好("different things should look different" 设计原则,它也在 Perl 中反复出现)。看起来与众不同的最明显方式是......没有印记。

但是,出于句法原因,签名中需要一些东西来消除从类型中引入的名称的歧义(因为像 (Foo) 这样的签名可以匹配类型 Foo 但忽略值已经得到支持并且很有用,我们不想失去它)。 \ 被选中扮演那个角色,得到 sub foo(\x) { },然后允许在子程序内部使用 x

我的回忆是,在 my 的情况下允许使用此表单的时间稍晚一些,但我对此并不完全确定。关于不承诺行为的无符号符号的重要事情之一是它也不承诺 assignment 行为,因此它上面的 = 有点多后期绑定(编译器可能会考虑印记,并为 $/&@/% 分配发出完全不同的代码)。当然,如果符号绑定了一个值,那么就不能赋值了。

这就剩下绑定行为的问题了。如其他答案之一所述,决定使无符号符号形成 "static single assignment" 语法。这有多种原因,包括:

  • 帮助保持印记的 "data stands out" 可读性 属性,至少确保任何(立即)可变的东西仍然带有印记
  • 还通过让 reader 知道该符号永远不会反弹到新值的形式来增强程序的可读性,因此可以有效地将其视为范围内的常量。这与在没有印记的情况下很容易声明真实的 constants 是一致的。
  • 温和地鼓励一种更实用的风格(也许通过让那些决定讨厌印记的人用至少比印记提供的代码更易读的代码来回报我们其他人:-))

最后,我要指出的是,我发现 "sigilless variable" 这个词有点误导,因为它没有任何变数。它是一种引入符号的语法,该符号已初始化绑定到特定事物,并且始终适用于该符号的(词汇)生命周期。考虑它们的最佳方式可能是将它们与变量区别开来——这意味着存储——而只是将它们视为一种将名称附加到值的方法。