Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

raku

rakudo

I suspect this may be a bug in Rakudo, but I just started playing with Perl 6 today, so there's a good chance I'm just making a mistake. In this simple program, declaring a typed array inside a sub appears to make the Perl 6 compiler angry. Removing the type annotation on the array gets rid of the compiler error.

Here's a simple prime number finding program:

#!/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);

On Rakudo Star 2016.07.1 (from the Fedora 24 repos), this program gives the following error:

[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

If I remove the type annotation on the vals array, the program works correctly:

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

Am I making a mistake in my usage of Perl 6, or is this a bug in Rakudo?

like image 537
Sultan Avatar asked Aug 18 '16 20:08

Sultan


1 Answers

There's a potential error in your code that's caught by type checking

The error message you got draws attention to line 8:

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

This line assigns the list of values on the right of the = to the list of elements on the left.

The first element in the list on the left, @vals[2*$i], gets a zero.

You didn't define any more values on the right so the rest of the elements on the left are assigned a Mu. Mus work nicely as placeholders for elements that do not have a specific type and do not have a specific value. Think of a Mu as being, among other things, like a Null, except that it's type safe.

You get the same scenario with this golfed version:

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

As you've seen, everything works fine when you do not specify an explicit type constraint for the elements of the @vals array.

This is because the default type constraint for array elements is Mu. So assigning a Mu to an element is fine.


If you felt it tightened up your code you could explicitly assign zeroes:

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

This generates a (lazy) infinite list of zeroes on the RHS so that zero is assigned to each of the list of elements on the LHS.

With just this change your code will work even if you specify a type constraint for @vals.


If you don't introduce the xx Inf but do specify an element type constraint for @vals that isn't Mu, then your code will fail a type check if you attempt to assign a Mu to an element of @vals.

The type check failure will come in one of two flavors depending on whether you're using object types or native types.

If you specify an object type constraint (eg Int):

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

then you get an error something like this:

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

If you specify a native type constraint (eg int rather than Int):

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

then the compiler first tries to produce a suitable native value from the object value (this is called "unboxing") before attempting a type check. But there is no suitable native value corresponding to the object value (Mu). So the compiler complains that it can not even unbox the value. Finally, as hinted at at the start, while Mu works great as a type safe Null, that's just one facet of Mu. Another is that it's a "type object". So the error message is Cannot unbox a type object.

like image 180
raiph Avatar answered Jan 03 '23 05:01

raiph