重新绑定的规则是什么?

What are the rules for re-binding?

[注意:我问这个问题是基于旧版本的 Rakudo。如 , the confusing output was the result of Rakudo bugs, which have now been resolved 中所述。我留下了下面 Q 的原始版本以供历史参考。]

Raku 有时会禁止重新绑定;以下两行

sub f($a) { $a := 42 }
my \var = 'foo'; var := 'not-foo';

产生编译时错误:

===SORRY!=== Error while compiling 
Cannot use bind operator with this left-hand side

然而,Raku 允许在很多很多情况下重新绑定——包括许多让我大吃一惊的情况。以下所有成功重新绑定;每个 say 输出 not-foo.

my Any \a = 'foo';
say a := 'not-foo';
my Any $b := 'foo';
say $b := 'not-foo';
my @c := ('foo', 'foo');
say @c := ('not-foo', 'not-foo');
my @d is List = ('foo', 'foo');
say @d := ('not-foo', 'not-foo');
my %e := (:foo<foo>);
say %e := (:not-foo<not-foo>);

sub fn1(Any \a) { a := 'not-foo';  say a  }
fn1 'foo';
sub fn2(Any $b) { $b := 'not-foo'; say $b }
fn2 'foo';
sub fn3(@c) {  @c := ('not-foo', 'not-foo'); say @c }
fn3 ('foo', 'foo');
sub fn4(+@d) { @d := ('not-foo', 'not-foo'); say @d }
fn4 ('foo', 'foo');
sub fn5(@d is raw) { @d := ('not-foo', 'not-foo'); say @d }
fn5 ('foo', 'foo');

my ($one-foo, $two-foo) := ('foo', 'foo');
$one-foo := 'not-foo';
say $one-foo;

my \foo = 'foo';
say MY::<foo> := 'not-foo';
sub foo-fn { 'foo' }
MY::<&foo-fn> := { 'not-foo' }
say foo-fn;

my $absolutely-foo = 'foo';
sub fn6 { CALLER::<$absolutely-foo> := 'not-foo';}
fn6;
say $absolutely-foo;

因此,如果满足以下任一条件,则似乎当前允许重新绑定任何名称,无论是否有印记:

  1. 该名称有任何明确的类型约束(包括 Any@% 符号强加的类型约束),或
  2. 重新绑定使用了限定名称。

此重新绑定当前发生在声明的变量和参数上,并且包括非 rwcopy 的参数。正如最后一个示例所示,它甚至允许以(似乎?)违反词法范围的方式重新绑定。 (该示例基于 a Roast test,并用注释 -- legal? 进行了注释,这表明我可能至少不是唯一一个发现这种行为令人惊讶的人!虽然测试重新绑定了 is dynamic变量——在某些方面,上述行为更令人惊讶。

据我所知,唯一无法使用其中一种方法重新绑定的名称是声明为 constant.

的名称

所以四个问题:

  1. 我描述的当前行为是否正确? [编辑:也就是说,我上面列出的两条规则是否正确描述了当前行为,或者正确的描述是否需要 other/additional 条规则?]
  2. 这种行为 correct/intentional/in 符合规范吗? (尽管存在 S03-binding,但我在 rebinding 上发现的很少。
  3. 如果此行为不是故意的,重新绑定的规则应该是什么?
  4. 有什么方法可以告诉 Raku“不要将此名称重新绑定到新值,不是真的”?

(这个问题取代了 , which I asked before I realized how easy it is to re-bind name; I'm closing it in favor of this one. Another related question: ,它讨论了一些设计权衡,假设无符号变量 不能 被重新绑定,这与几个例子相反以上。)

绝对不权威的回答。奇怪的是,就像您之前 Q 中的 jnthn 一样,以相反的顺序回答您的问题感觉很自然:

Is there any way to tell Raku "don't rebind this name to a new value, no-really-I-mean-it"?

据我所知——纯粹是通过测试变体,而不是通过检查 roast 或编译器源代码——你可以而且必须声明一个 sigil free 符号,它不能是用 [= 声明的符号13=]:

constant foo = 42;
class bar {}
enum baz <a b>;
subset boo;

据我所知,上述声明在编译它们的封闭组件期间将它们的符号永久绑定到它们声明的值,并且修改这些符号的尝试,例如 MY::<foo> := 99;,将被默默地忽略。


请注意,使用 constant 并未解决绑定值是否不可变的问题:

constant foo = [42];
foo[1] = 99;
say foo; # [42 99]

如果您想要绝对不变性,一个绝对不变的绑定是不够的。您必须确保绑定 object/value 也是绝对不可变的。


请注意,这声明了一个 sigil'd 标识符,因此您可以修改它绑定的内容:

sub f { say 'foo' }
MY::<&f> := { say 'boo' }
f; # boo

If this behavior is not intentional, what are the rules about rebinding supposed to be?

我的猜测,就其价值而言,一切都如其所愿。


几个星期前,为了回应你表明可以重新绑定一个用 my Int \foo = 42; 声明的无符号变量,我写道:

Right now what you've shown in this Q smashed my mental model

但那是当时。而“现在”暗示了我在 Raku 的经历中学会珍惜的东西,即使是现在,在第一次认真进入它 10 年后。每次我遇到这样的惊喜时,我都记得我会被很好地建议要保持从根本上改变我的想法的开放态度。果然,正如我在此答案的早期版本中所写:

I've concluded in retrospect that it all makes perfect Rakunian sense to me.

嗯?

  • 发生什么事打破了我的心智模型? 我形成了(更糟的是,传播了/o)一个不正确的心智模型,认为无符号变量必然是 Static Single Assignment。然后我遇到了一些在精神上很有趣但不适合该模型的代码。粉碎!

  • 之后发生了什么? 我得出结论,SSA 方面仅适用于使用 constant 声明的无符号变量。而我的“崩溃”反应是由于我长期以来持有错误的思维模式。无论如何,虽然我现在必须为我与其他误导的 Rakoons 合作向像你这样的无辜民众传播错误的思维模式的罪过赎罪,但我需要快速彻底地克服它。所以然后我睁开眼睛,意识到 Raku 只是 Rakunian。

  • “Rakunian”是什么意思? 我认为 Larry Wall 得到 the late, great John Shutt's point about Irregularity in language:

    Irregularity is a natural consequence of the impedance mismatch between the formal structure of language and the sapient semantics communicated through it ... large parts of a language, relatively far from its semantic core, may be tolerably regular, but the closer things get to its semantic core, the more often they call for variant structure. It may even be advantageous for elements near the core to be just slightly out of tune with each other, so they create (to use another physics metaphor) a complex interference pattern that can be exploited to slip sapient-semantic notions through the formal structure.

    (即使 Larry 不像 John 那样看待事物,但我认为这是 Raku 语义核心的基本原则之一。好吧,不是 Raku 的语义核心底层意义 。但是这里我们关注的是 Raku 当前的标准 表面句法方言 ,所以我在那个上下文中谈论“语义核心”。)

    我想说声明和改变(或不改变)标识符的值接近 Raku 标准方言的语义核心,并且 Raku 的相关变体在有用的方面彼此略有不协调。具体来说,在声明无符号标识符变量的上下文中:

    • = 总是 绑定 而不是 分配 .

    • constant foo = ...; 在编译时不可变地绑定到一个值。 (我确实对霍夫曼编码感到奇怪。我一直在思考新的声明符 aanthe,其中 aanmy constanttheour constant 的别名,这就是 constant 在没有范围声明符前缀的情况下的意思。)

    • my \foo = ...; 在 运行 时绑定;可以通过使用 :=MY 引用来 重新 绑定。

    • my Type \foo = ...; 类似于 my \foo...; 但也可以使用 :=.

      反弹

Is that behavior correct/intentional/in line with the spec? (Despite the presence of S03-binding, I've found remarkably little on rebinding).

“spec”的官方定义是roast (repository of a ll s规格tests).

正式发布 Rakudo 编译器版本的一部分是确保它已通过 roast。

因此,如果您使用正式发布的 Rakudo 编译并 运行 代码,则行为符合规范。


Am I correctly describing the current behavior?

你已经提供了编译和 运行s 的代码,所以根据这个定义,这是唯一理智的 imo,代码确实正确地描述了当前的行为,其中“当前”意味着版本正在使用的编译器(反过来将针对某些特定版本的 roast 进行测试,哪个版本应该 iirc git 标记为对应于 Raku 语言的特定官方修订版)。

The name has any explicit type constraint (including Any and the type constraints imposed by the @ or % sigils), or

我认为类型约束在这里不相关。 Raku 中的每个容器都有隐式或显式类型约束(即使它是 Mu)。通过在它前面放一个 Any 来使它可重新绑定,这会散发出 bug 的味道。

相反,我认为规则是您可以重新绑定一个名称,除非编译器知道它是只读的。

最重要的是,已知以下案例仅就绪:

  • block/routine 参数
  • 词汇替换(即 &fsub f ... 之后)
  • 任何无符号名称(my \x = ...class A { ... }constant a = ...

我在问题中询问的不一致行为是 Rakudo 错误的结果 – Rakudo 在某些不应该的情况下允许重新绑定。此错误已在 Rakudo/Rakudo#4536 中解决。

本次决议后,重新绑定规则为:

  • Sigilless "variables"不能反弹(也不能重新分配,所以它们不是真正的“变量”)
  • 带有印记的变量通常可以反弹,但有以下例外情况。
  • 如果一个 sigiled 变量是 Signature, then it cannot be rebound unless it is declared to be rebindable via the is copy or is rw traits 的一部分。
    • 这适用于函数的签名(因此 sub f($a) { $a := 42 } 是非法的)
    • 它也适用于使用 := 作为变量声明的一部分解构的签名。例如,在 my ($var1, $var2) := ('foo', 'bar') 中,right-hand 面是签名,因此 $var1$var2 不能反弹。

应用这些规则意味着以下所有重新绑定(在提出问题时允许)现在都被禁止:

my Any \a = 'foo';
say a := 'not-foo';

sub fn1(Any \a) { a := 'not-foo';  say a  }
fn1 'foo';
sub fn2(Any $b) { $b := 'not-foo'; say $b }
fn2 'foo';
sub fn3(@c) {  @c := ('not-foo', 'not-foo'); say @c }
fn3 ('foo', 'foo');
sub fn4(+@d) { @d := ('not-foo', 'not-foo'); say @d }
fn4 ('foo', 'foo');
sub fn5(@d is raw) { @d := ('not-foo', 'not-foo'); say @d }
fn5 ('foo', 'foo');

my ($one-foo, $two-foo) := ('foo', 'foo');
$one-foo := 'not-foo';
say $one-foo;

相反,应用这些规则意味着(正确地)允许问题中显示的一些其他重新绑定:

my Any $b := 'foo';
say $b := 'not-foo';
my @c := ('foo', 'foo');
say @c := ('not-foo', 'not-foo');
my @d is List = ('foo', 'foo');
say @d := ('not-foo', 'not-foo');
my %e := (:foo<foo>);
say %e := (:not-foo<not-foo>);

最后,问题中显示的一些示例涉及修改 (pseudo) package 的内容。这允许重新绑定,否则会被上述规则禁止:

my \foo = 'foo';
say MY::<foo> := 'not-foo';
sub foo-fn { 'foo' }
MY::<&foo-fn> := { 'not-foo' }
say foo-fn;

my $absolutely-foo = 'foo';
sub fn6 { CALLER::<$absolutely-foo> := 'not-foo';}
fn6;
say $absolutely-foo;

但是,与使用 the Metaobject Protocol to access private methods/attributes, using pseudo packages to break Raku's normal rules should be an extreme last resort 一样,不享有与 Raku 其他方面相同的稳定性保证。