Perl 6 сообщает "Не удается распаковать объект типа" при наборе массива

Я подозреваю, что это может быть ошибкой в ​​Rakudo, но я только начал играть с Perl 6 сегодня, так что есть большая вероятность, что я просто ошибаюсь. В этой простой программе объявление типизированного массива внутри подпрограммы, кажется, разозлит компилятор 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?

1 ответ

Решение

В вашем коде есть потенциальная ошибка, которая обнаруживается при проверке типа

Полученное сообщение об ошибке привлекает внимание к строке 8:

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

Эта строка назначает список значений справа от = к списку элементов слева.

Первый элемент в списке слева, @vals[2*$i], получает ноль.

Вы не определили больше значений справа, поэтому остальным элементам слева присваивается Mu, Mu хорошо работают в качестве заполнителей для элементов, которые не имеют определенного типа и не имеют определенного значения. Думать о 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, Другое дело, что это "тип объекта". Так что сообщение об ошибке Cannot unbox a type object,

Другие вопросы по тегам