Perl 6 在键入数组时报告 "Cannot unbox a type object"

Perl 6 reports "Cannot unbox a type object" when typing an array

我怀疑这可能是 Rakudo 中的错误,但我今天才开始玩 Perl 6,所以我很可能只是犯了一个错误。在这个简单的程序中,在 sub 中声明类型化数组似乎会使 Perl 6 编译器生气。删除数组上的类型注释可以消除编译器错误。

这是一个简单的素数查找程序:

#!/usr/bin/env perl6
use v6;

sub primes(int $max) {
    my int @vals = ^$max; # forcing a type on vals causes compiler error (bug?)
    for 2..floor(sqrt($max)) -> $i {
        next if not @vals[$i];
        @vals[2*$i, 3*$i ... $max-1] = 0;
    }
    return ($_ if .Bool for @vals)[1..*];
}

say primes(1000);

在 Rakudo Star 2016.07.1(来自 Fedora 24 存储库)上,此程序出现以下错误:

[sultan@localhost p6test]$ perl6 primes.p6 
Cannot unbox a type object
  in sub primes at primes.p6 line 8
  in block <unit> at primes.p6 line 13

如果我删除 vals 数组上的类型注释,程序将正常运行:

    ...
    my @vals = ^$max; # I removed the int type
    ...

我是不是在使用 Perl 6 时犯了错误,或者这是 Rakudo 中的错误?

类型检查发现您的代码中存在潜在错误

您收到的错误消息引起了对第 8 行的注意:

@vals[2*$i, 3*$i ... $max-1] = 0;

此行将 = 右侧的值列表分配给左侧的元素列表。

左侧列表中的第一个元素 @vals[2*$i] 为零。

您没有在右边定义更多的值,所以左边的其余元素被分配了 MuMus 可以很好地用作没有特定类型且没有特定值的元素的占位符。将 Mu 视为除其他外类似于 Null,只是它是类型安全的。

你会得到与这个高尔夫版本相同的场景:

my @vals;
@vals[0,1] = 0; # assigns 0 to @vals[0], Mu to @vals[1]

如您所见,当您 @vals 数组的元素指定显式类型约束时,一切正常。

这是因为数组元素的默认类型约束是 Mu。因此,将 Mu 分配给一个元素是可以的。


如果您觉得它加强了您的代码,您可以显式分配零:

@vals[2*$i, 3*$i ... $max-1] = 0 xx Inf;

这会在 RHS 上生成一个(惰性)无限零列表,以便将零分配给 LHS 上的每个元素列表。

仅通过此更改,即使您为 @vals.

指定了类型约束,您的代码也能正常工作

如果您引入xx Inf引入@vals指定元素类型约束那不是 Mu,那么如果您尝试将 Mu 分配给 @vals.

的元素,您的代码将无法通过类型检查

类型检查失败会出现两种情况之一,具体取决于您使用的是对象类型还是本机类型。

如果指定对象类型约束(例如Int):

my Int @vals;
@vals[0,1] = 0;

然后你会得到类似这样的错误:

Type check failed in assignment to @vals; expected Int but got Mu (Mu)

如果指定本机类型约束(例如 int 而不是 Int):

my int @vals;
@vals[0,1] = 0;

然后编译器在尝试类型检查之前首先尝试从对象值生成合适的本机值(这称为“拆箱”)。但是没有合适的原生值对应对象值(Mu)。所以编译器抱怨它甚至不能拆箱值。最后,正如一开始所暗示的,虽然 Mu 作为类型安全的 Null 工作得很好,但这只是 Mu 的一个方面。另一个是它是 "type object"。所以错误信息是Cannot unbox a type object.