Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Perl 6, can I use an Array as a Hash key?

In the Hash documentation, the section on Object keys seems to imply that you can use any type as a Hash key as long as you indicate but I am having trouble when trying to use an array as the key:

> my %h{Array};
{}
> %h{[1,2]} = [3,4];
Type check failed in binding to parameter 'key'; expected Array but got Int (1)
in block <unit> at <unknown file> line 1

Is it possible to do this?

like image 665
Hunter McMillen Avatar asked Apr 26 '18 20:04

Hunter McMillen


People also ask

Can an array be a hash key?

All lists, mutable or not, hash by value. And you can use e.g. mutable ArrayList as hash keys.

Can Perl hash value be an array?

You can create arrays of hashes, hashes of arrays, and any other sort of complicated data structure you can dream up. To learn more about these, look at the Perl documentation.

Is an array a hash?

A Hash is a collection of key-value pairs. It is similar to an Array, except that indexing is done via arbitrary keys of any object type, not an integer index. Hashes enumerate their values in the order that the corresponding keys were inserted.


2 Answers

The [1,2] inside the %h{[1,2]} = [3,4] is interpreted as a slice. So it tries to assign %h{1} and %{2}. And since the key must be an Array, that does not typecheck well. Which is what the error message is telling you.

If you itemize the array, it "does" work:

my %h{Array};
%h{ $[1,2] } = [3,4];
say %h.perl;  # (my Any %{Array} = ([1, 2]) => $[3, 4])

However, that probably does not get what you want, because:

say %h{ $[1,2] };  # (Any)

That's because object hashes use the value of the .WHICH method as the key in the underlying array.

say [1,2].WHICH; say [1,2].WHICH;
# Array|140324137953800
# Array|140324137962312

Note that the .WHICH values for those seemingly identical arrays are different. That's because Arrays are mutable. As Lists can be, so that's not really going to work.

So what are you trying to achieve? If the order of the values in the array is not important, you can probably use Sets as keys:

say [1,2].Set.WHICH; say [1,2].Set.WHICH
# Set|AEA2F4CA275C3FE01D5709F416F895F283302FA2
# Set|AEA2F4CA275C3FE01D5709F416F895F283302FA2

Note that these two .WHICHes are the same. So you could maybe write this as:

my %h{Set};
dd %h{ (1,2).Set } = (3,4); # $(3, 4)
dd %h; # (my Any %{Set} = ((2,1).Set) => $(3, 4))

Hope this clarifies things. More info at: https://docs.raku.org/routine/WHICH

like image 88
Elizabeth Mattijsen Avatar answered Oct 16 '22 15:10

Elizabeth Mattijsen


If you are really only interested in use of an Object Hash for some reason, refer to Liz's answer here and especially the answers to, and comments on, a similar earlier question.

The (final1) focus of this answer is a simple way to use an Array like [1,'abc',[3/4,Mu,["more",5e6],9.9],"It's {<sunny rainy>.pick} today"] as a regular string hash key.

The basic principle is use of .perl to approximate an immutable "value type" array until such time as there is a canonical immutable Positional type with a more robust value type .WHICH.

A simple way to use an array as a hash key

my %hash;
%hash{ [1,2,3].perl } = 'foo';
say %hash{ [1,2,3].perl }; # displays 'foo'

.perl converts its argument to a string of Perl 6 code that's a literal version of that argument.

say [1,2,3].perl;    # displays '[1, 2, 3]'

Note how spaces have been added but that doesn't matter.

This isn't a perfect solution. You'll obviously get broken results if you mutate the array between key accesses. Less obviously you'll get broken results corresponding to any limitations or bugs in .perl:

say [my %foo{Array},42].perl; # displays '[(my Any %{Array}), 42]'

1 This is, hopefully, the end of my final final answer to your question. See my earlier 10th (!!) version of this answer for discussion of the alternative of using prefix ~ to achieve a more limited but similar effect and/or to try make some sense of my exchange with Liz in the comments below.

like image 43
raiph Avatar answered Oct 16 '22 16:10

raiph