Typed array from non-typed array

Viewed 180

Let's say we have the following multi sub:

multi sub abc(Int        @array) { say 10, ' ', @array; }

multi sub abc(Array[Int] @array) { say 20, ' ', @array; }

multi sub abc(Str        @array) { say 30, ' ', @array; }

multi sub abc(Array[Str] @array) { say 40, ' ', @array; }

As mentioned in this question, calling these with typed arrays can get verbose:

abc Array[Int].new([1,2,3]);

abc Array[Array[Int]].new([Array[Int].new([1,2,3]), Array[Int].new([2,3,4])]);

It would be nice if the type could be inferred from the literal such that we could do something like this:

abc typed([1,2,3]);

abc typed([[1,2,3],[2,3,4]]);

abc typed(['a', 'b', 'c']);

abc typed([['a', 'b', 'c'], ['b', 'c', 'd']]);

Going further, let's add a clause which does the type inference for us:

multi sub abc(@array) { abc typed(@array); }

Now we can get the full inference with no extra imposed syntax:

abc [1,2,3];

abc [[1,2,3],[2,3,4]];

abc ['a', 'b', 'c'];

abc [['a', 'b', 'c'], ['b', 'c', 'd']];

The above displays the following:

10 [1 2 3]
20 [[1 2 3] [2 3 4]]
30 [a b c]
40 [[a b c] [b c d]]

Below is a simple version of typed which works on:

  • Array[Int]
  • Array[Array[Int]]
  • Array[Str]
  • Array[Array[Str]]

My question is, how would you go about implementing this sort of type inference? Is there a better approach? Is there already a similar facility available?

sub type-of(\obj)
{
    if obj.^name eq 'Array'
    {
        if obj.map({ type-of($_).^name }).all eq obj.map({ type-of($_).^name })[0]
        {
            my $type = type-of(obj[0]);
            return Array[$type];
        }
        return Array;
    }

    if obj.^name eq 'Int' { return Int; }
    if obj.^name eq 'Str' { return Str; }
}

sub typed(\obj)
{
    if obj.^name eq 'Array'
    {
        return type-of(obj)(obj.List.map({ $_.&typed }).Array);
    }

    return (type-of(obj))(obj);
}
1 Answers
Related