"Cannot assign to immutable value" 尝试分配给字符串 + 角色时

"Cannot assign to immutable value" when trying to assign to a string + role

the example in the Iterable doc page

开始
role DNA does Iterable {
  method iterator(){ self.comb.iterator }
};

my @a does DNA = 'GAATCC';
.say for @a; # OUTPUT: «G␤A␤A␤T␤C␤C␤» 

我发现它使用 @ 声明很奇怪,所以我将其更改为 natural 声明字符串的方式,$:

my $a does DNA = 'GAATCC';

但这失败了,有点令人困惑 "Cannot assign to an immutable value"。不需要现场分配,所以我们可以这样做:

my $a = 'GAATCC';
$a does DNA;
.say for $a;

这只是留待以后混入。但这只是打印字符串,而没有注意 Iterable 混入。让我们明确地称呼它:

.say for $a.iterator;

它做的事情和以前一样,只是它打印了 $a.iterator 的值,而不实际调用函数:

<anon|69>.new

这看起来像 。基本问题是我不明白 Iterable 的真正作用是什么,for 真正做了什么,以及它何时在某个对象上调用 iterator。有什么想法吗?

我认为这条线并不像您认为的那样:

my @a does DNA = 'GAATCC';

等同于:

my @a := [ 'GAATCC', ];
@a does DNA;

基本上 .comb 调用将数组强制转换为 Str,并将其拆分为字符。


如果您这样做:

my @a = 'GAATCC' but DNA;

基本相同
my @a := Seq.new(('GAATCC' but DNA).iterator).Array;

请注意 @ 变量存储 Positional 值而不是 Iterable 值。


你想要的是

my $a = 'GAATCC' but DNA;

$a.map: &say;

如果你想使用 for,你不能使用带有 $ 印记的变量

my \a = 'GAATCC' but DNA;

.say for a;

您可能希望将 Seq list List 等方法添加到 DNA

role DNA does Iterable {
  method iterator(){
    self.comb.iterator
  }
  method Seq(){
    Seq.new: self.iterator
  }
  method list(){
    # self.Seq.list
    List.from-iterator: self.iterator
  }
}

my $a = 'GAATCC' but DNA;
.say for @$a;

您的问题标题指向一个错误。此答案涵盖了错误以及您提出的其他隐式和显式问题。

背景

fails with a somewhat bewildering "Cannot assign to an immutable value".

我认为这是一个错误。让我们从一些有效的代码开始:

my $a = 42;
say $a;          # 42
say WHAT $a;     # (Int)               type of VALUE currently ASSIGNED to $a
say WHAT VAR $a; # (Scalar)            type of VARIABLE currently BOUND to $a
$a = 42;         # works fine

my 声明中 $a 绑定到一个新的 Scalar containerScalar 容器通常会隐藏自己。如果你问 WHAT type $a 是什么,你实际上得到了当前分配给 Scalar 的值的类型(它的值是 "contains")。您需要 VAR 才能访问绑定到 $a 的 容器 。当您使用 = 分配给 Scalar 容器时,您将分配的值 复制到 容器中。

role foo {}
$a does foo;     # changes the VALUE currently ASSIGNED to $a
                 # (NOT the VARIABLE that is BOUND to $a)
say $a;          # 42                  mixed in `foo` role is invisible
say WHAT $a;     # (Int+{foo})         type of VALUE currently ASSIGNED to $a
say WHAT VAR $a; # (Scalar)            type of VARIABLE currently BOUND to $a
$a = 99; say $a; # 99

doesfoo 角色混合到 42 中。您仍然可以分配给 $a 因为它仍然绑定到 Scalar.

请注意 does 的这两种用法有何不同的效果:

my $a does foo;  # mixes `foo` into VARIABLE bound to $a
$a does foo;     # mixes `foo` into VALUE assigned to $a

错误

$a.VAR does foo; # changes VARIABLE currently BOUND to $a (and it loses the 42)
say $a;          # Scalar+{foo}.new    VALUE currently ASSIGNED to $a
say WHAT $a;     # (Scalar+{foo})      type of VALUE currently ASSIGNED to $a
say WHAT VAR $a; # (Scalar+{foo})      type of VARIABLE currently BOUND to $a

$a = 'uhoh';     # Cannot assign to an immutable value

doesfoo 角色混合到绑定到 $a 的 Scalar 中。似乎带有 mixin 的 Scalar 不再成功用作容器并且分配失败。

目前我认为这是一个错误。

my $b does foo;  # BINDS mixed in VARIABLE to $b
$b = 'uhoh';     # Cannot assign to an immutable value

my $b does foomy $b; $b.VAR does foo; 的结果相同,因此您会遇到与上述相同的问题。

其他让您感到困惑的事情

my $a = 'GAATCC';
$a does DNA;
.say for $a;

just prints the string, without paying any attention to the Iterable mixin.

因为 $a VARIABLE 仍然绑定到 Scalar(如上文 Background 部分所述),现在具有 DNA 混入的角色与 the decision process for uses about whether to call its argument's .iterator method.

无关

Let's call it then explicitly ... prints the value of $a.iterator, without actually calling the function:

.say for $a.iterator;

嗯,它会调用您的 DNA 角色的 .iterator 方法。但是在 DNA 角色的 iterator 方法返回的 self.comb 末尾有 另一个 .iterator 调用,所以你 .say中学 .iterator.

今天有效的解决方案

我认为 Brad 的回答很好地涵盖了您的大部分选择。

如果你想使用 $ 印记,我认为你的好 [=7​​3=] 与今天的 P6 一样好。

一种可能有一天会奏效的解决方案

在理想世界中,P6 设计中的所有优点都将在 6.c 和 Perl 6 的 Rakudo 编译器实现中得到充分实现。也许这将包括编写此代码并获得您想要的东西的能力:

class DNA is Scalar does Iterable { ... }
my $a is DNA = 'GAATCC';
.say for $a;

... 代码与要点中的代码大致相同,只是 DNA class 将是标量容器,因此 new 方法将改为 STORE 方法或类似方法,当使用 =.[=74= 将值分配给容器时,它将传递的值分配给 $!value 属性或某些类似的方法]

但相反,您得到:

is trait on $-sigil variable not yet implemented. Sorry.

因此,您今天最接近 = 'string' 的理想更改 $a 就是像您在要点中所做的那样使用 := DNA.new('string') 进行绑定。

请注意,您 可以 将任意复合容器绑定到 @% 印记变量。所以你可以看到事情最终应该如何运作。