【问题标题】:What are the rules for re-binding?重新绑定的规则是什么?
【发布时间】:2021-11-12 19:24:48
【问题描述】:

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@% sigils 施加的类型约束),或者
  2. 重新绑定使用限定名称。

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

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

那么四个问题:

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

(这个问题取代了my earlier question,在我意识到重新绑定名称是多么容易之前我问过这个问题;我正在关闭它以支持这个问题。另一个相关问题:Is there a purpose or benefit in prohibiting sigilless variables from rebinding?,它讨论了一些与上面的几个例子相反,设计权衡是假设无符号变量不能重新绑定。)

【问题讨论】:

    标签: immutability raku lexical-scope rakudo variable-binding


    【解决方案1】:

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

    有什么方法可以告诉 Raku“不要将此名称重新绑定到新值,不是真的-我是故意的”?

    据我所知——纯粹是通过测试变体,而不是通过检查烤或编译器源——你可以而且必须声明一个无印记符号,它不能是用 my \symbol ... 声明的符号:

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

    据我所知,上述声明在编译其封闭的 compunit 期间将其符号永久绑定到其声明的值,并且尝试修改这些符号,例如 MY::&lt;foo&gt; := 99;,会被忽略。


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

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

    如果您想要绝对不变性,那么绝对不可变的绑定是不够的。您必须确保绑定的对象/值也是绝对不可变的。


    请注意,这声明了一个带有印记的标识符,因此您可以修改它的绑定内容:

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

    如果这种行为不是故意的,那么重新绑定的规则应该是什么?

    我的猜测是,一切都像它应该的那样。


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

    现在你在这个 Q 中展示的内容打破了我的心理模型

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

    回想起来,我得出的结论是,这一切对我来说都是完美的 Rakunian 意义。

    嗯?

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

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

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

      不规则性是语言的形式结构与通过它传达的智能语义之间的阻抗不匹配的自然结果......语言的大部分,相对远离其语义核心,可能是可以容忍的规则,但更接近的东西到达它的语义核心,他们越经常需要变体结构。靠近核心的元素彼此稍微失调甚至可能是有利的,因此它们创建(使用另一个物理比喻)复杂的干涉图案,可以利用该图案将智能语义概念滑过形式结构。

      (即使拉里像约翰那样看待事物,我也这样做,并认为这是 Raku 语义核心的基本原则之一。嗯,不是 Raku 的语义核心,在底层 @ 的意义上987654323@。但这里我们关注的是 Raku 当前的标准表面句法方言,所以我在这种情况下谈论的是“语义核心”。)

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

      • = 总是绑定而不是赋值

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

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

      • my Type \foo = ...;my \foo...; 类似,但也可以使用:= 反弹。


    这种行为是否正确/有意/符合规范? (尽管存在 S03 绑定,但我发现重新绑定的情况非常少)。

    “规范”的官方定义是烤(repository of all specification t测试)。

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

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


    我是否正确地描述了当前的行为?

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

    【讨论】:

    • 当您说“如果您使用正式发布的 Rakudo 编译和运行代码,则行为符合规范”时,我不太确定您是多么严肃/口齿不清。当然,这在某种狭义和字面意义上是正确的——但在我们发现 Rakudo 中的错误的同时发现 Roast 中的错误也非常普遍(并通过协调的 PR 修复两者)。 Roast 中的一个错误是规范没有说明它的含义,因此不符合规范的行为仍然通过。我认为这就是这里发生的事情。
    • 你提到“我回想起来,[重新绑定行为]对我来说都是完美的 Rakunian 意义”。你能扩展一下吗?除非我遗漏了什么,否则您的回答无法解释为什么这种行为具有完美的 Rakunian 意义,并且在 我的 头脑中没有任何意义! :)
    • 我非常认真地将烤肉作为官方规格。 “在 Roast 中发现错误非常常见”。您的意思是遗漏还是错误的东西,并且在 PR 中更改了,而不是添加新测试
    • “您的意思是在 PR [或] 添加新测试中更改的 [某事]?”我认为两者都很常见,但我主要是在考虑添加新测试。查看Roast commit history,最近的提交添加了一个与callsame 相关的测试。我想知道是否需要添加类似的重新绑定测试。现在,允许重新绑定(如 Rakudo)成功通过了 Roast。通过阅读测试,我相信禁止(大多数)重新绑定也会通过,所以我认为 Roast 可以(应该?)指定一种或另一种方式。
    • (我在聊天中回复了你的评论。)WTF?现在它删除了聊天。 :( 好的,从我的历史中恢复:chat.stackoverflow.com/rooms/237343/…Aiui 几天后会被删除,但请尽可能阅读。或查看gist.github.com/raiph/faf0bfbbf593c8bc1b1f3385d2f815de
    【解决方案2】:

    名称具有任何显式类型约束(包括 Any 和由 @ 或 % 符号施加的类型约束),或

    我认为类型约束在这里不应该是相关的。 Raku 中的每个容器都有一个隐式或显式类型约束(即使它是Mu)。通过将 Any 放在它前面来使事物可重新绑定是一个错误。

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

    在我的帽子顶部,已知以下情况仅准备就绪:

    • 块/例程参数
    • 词法替换(即&amp;fsub f ... 之后)
    • 任何无符号名称(my \x = ...class A { ... }constant a = ...

    【讨论】:

    • 目前,sub f($a) { }sub g(Mu $b) { } 都具有只读参数(如参数对象上的 .readonly 方法所示),但 $b 是可重新绑定的,而 $a 不是。根据您的编辑,听起来您认为存在 Rakudo 错误并且 $a$b 都不应该是可重新绑定的。对吗?
    • @codesections 正确,我认为这是一个错误。
    • “现在,sub f($a) { }sub g(Mu $b) { } 都具有只读参数(如参数对象上的.readonly 方法所示)” “只读”一词是在谈论一个完全不同的概念来自binding,即assignment。 (有人可能会争辩说 assignment 和 binding 之间的区别是没有帮助的,但那艘船是 20 年前航行的。)如果选择将“readonly”解释为 英语 意义上的,那么大概需要考虑 change一般意义上的,包括例如constant a = []; a = 42; say a; # [42]sub foo(\a = []) { a = 42 }; say foo; # [42]
    • "你可以重新绑定一个名字,除非编译器知道它是只读的。" “只读”是什么意思?在constant a = [] 中是a“只读”吗? $asub foo ($a) {} 中是“只读”的吗? sub foo (@a) {} 中的 @a 怎么样?关于“bug”这个词有一个相关的歧义问题。我想我们都同意这里肯定有一个或多个“错误”,其中“错误”的意思是令人惊讶的事情。但这是由于我们当前的心智模型,还是由于在讨论、方法名称、错误消息和文档中不规范地使用“只读”,还是由于 Raku 行为中的一个或多个问题,或某种组合,或其他原因?跨度>
    • 一旦我克服了由于对my \foo...; 有错误的心理模型而经历的震惊,我得出结论,“这一切对我来说都是完美的 Rakunian 意义”,即:应该 称为“变量”的标识符是可变的吗?为了帮助新手理解这一点,Raku 将sub foo ($a) { $a = 42 } 设为错误(在运行时调用foo),将sub foo($a) { $a := 42 } 设为错误(在编译时)。同样,在class a {} 之后,a 不被称为“变量”,也不能重新声明。在考虑和学习“变量”含义的过程中,我会考虑那些有用的新手指南。
    猜你喜欢
    • 2012-10-09
    • 2014-02-03
    • 2011-06-18
    • 2018-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多