7

[注意:我根据旧版本的 Rakudo 提出了这个问题。正如在接受的答案中所解释的,令人困惑的输出是 Rakudo 错误的结果,这些错误现已得到解决。我留下了下面 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. 该名称具有任何显式类型约束(包括由或sigilsAny施加的类型约束),或@%
  2. 重新绑定使用限定名称。

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

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

所以四个问题:

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

(这个问题取代了我之前的问题,在我意识到重新绑定名称是多么容易之前,我问过这个问题;我正在关闭它以支持这个问题。另一个相关问题:禁止无符号变量是否有目的或好处rebinding?,它讨论了一些设计权衡,假设无符号变量不能被重新绑定,这与上面的几个示例相反。)

4

3 回答 3

4

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

有什么方法可以告诉 Raku“不要将这个名字重新绑定到一个新的值,不是真的,我的意思是它”?

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

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

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


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

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 合作向像你这样的无辜民众传播错误的心智模式而赎罪,但我需要快速彻底地克服它。于是我睁开眼睛,意识到拉库只是拉库尼亚人。

  • 我所说的“拉库尼亚人”是什么意思?我认为拉里·沃尔得到了约翰·舒特关于语言不规则性的已故伟大的观点

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

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

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

    • =总是绑定而不是分配

    • constant foo = ...;在编译时不可变地绑定到一个值。(我确实对霍夫曼编码感到好奇。我长期以来一直在思考新的声明符a,anthe, 其中aan是 的别名my constantthe是 的别名,如果没有范围声明符前缀our constant,这意味着什么。)constant

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

    • my Type \foo = ...;喜欢my \foo...;但也可以通过使用来反弹:=


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

“规格”官方定义是烘烤(所有规格测试的存储)。

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

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


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

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

于 2021-09-18T19:29:34.780 回答
3

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

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

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

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

  • 块/例程参数
  • 词汇分词(即&f在 之后sub f ...
  • 任何无符号名称 ( my \x = ..., class A { ... }, constant a = ...)
于 2021-09-23T05:51:13.160 回答
0

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

在此决议之后,重新绑定的规则是:

  • Sigilless“变量”不能被反弹(也不能被重新分配,所以它们不是真正的“变量”)
  • 带有印记的变量通常可以反弹,但以下例外情况除外。
  • 如果 sigiled 变量是Signature的一部分,则除非通过is copyoris rw特征声明它可以重新绑定,否则它不能被重新绑定。
    • 这适用于函数的签名(因此sub f($a) { $a := 42 }是非法的)
    • 它也适用于作为变量声明的一部分被解构的签名:=。例如,在 中my ($var1, $var2) := ('foo', 'bar'),右侧是签名,因此$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>);

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

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;

但是,就像使用元对象协议访问私有方法/属性一样,使用伪包来破坏 Raku 的正常规则应该是最后的极端手段,并且不像 Raku 的其他方面那样享有相同的稳定性保证。

于 2022-02-10T18:29:01.567 回答