Day 3 – Object Hashes

A short .promotional message: You may want to check out my Kickstarter campaign for Learning Perl 6, or the stuff I’m writing at the book’s website.

Perl 6 adds object hashes in which the keys aren’t merely strings. They are values and types put together. That means that the objects can stringify to the same thing but they can be different keys.

First, consider this normal hash construction. I’ll put a bunch of things into the hash then see what I got:

use v6;

my Int    $int     = 4;
my Str    $str     = "4";
my IntStr $int_str = <4>;  # Allomorph

my %hash;
%hash{$int}     = 'Plain old number';
%hash{$str}     = 'String of digits';
%hash{$int_str} = 'Dualvar';

say "There are ", %hash.elems, " elements in the hash";

# this calls the .gist method, sorta like a dumper routine
%hash.say;

The output shows that I only have one element in the hash and it’s the last one that I added:

There are 1 elements in the hash
{4 => Dualvar}

But, I can declare my hash in another way. I can declare it as an object hash by telling it what sort of objects I want it to accept. I can use the Any object which allows it to accept, well, anything:

my %hash{Any}; # accept any sort of object

This program is almost exactly the same but it acts much differently:

use v6;

my Int    $int     = 4;
my Str    $str     = "4";
my IntStr $int_str = <4>;  # Allomorph

my %hash{Any};
%hash{$int}     = 'Plain old number';
%hash{$str}     = 'String of digits';
%hash{$int_str} = 'Dualvar';

say "There are ", %hash.elems, " elements in the hash";

# this calls the .gist method, sorta like a dumper routine
%hash.say;

Now I see that I have three elements in the hash. It looks a bit strange in the .gist form because it appears that it has three keys that are all 4:

There are 3 elements in the hash
{4 => Dualvar, 4 => Plain old number, 4 => String of digits}

I can look at the .perl method that looks a bit more behind the scenes:

%hash.perl.say;

Now I see that there are three different sorts of objects:

There are 3 elements in the hash
(my Any %{Any} = IntStr.new(4, "4") => "Dualvar", 4 => "Plain old number", "4" => "String of dig<4>")

With an object hash, testing for existence is a bit different. It uses the .WHICH method, comparing the keys with the object identity operator ===.

So, I’ll put the same stuff in the hash then make a different object that I assign to $other_int. I check to see if $other_int is in the hash:

use v6;

my Int    $int     = 4;
my IntStr $int_str = <4>;  # Allomorph

my %hash{Any};
%hash{$int}     = 'Plain old number';
%hash{$int_str} = 'Dualvar';

my $other_int = 4;

# what are these things?
say "int: " ~ $int.WHICH;
say "other: " ~ $other_int.WHICH;

# are they the same?
say $int === $other_int ?? 'Same object' !! 'Different object';

# is it in the hash?
say %hash{$other_int}:exists ?? 'Other int exists in hash' !! 'Other int not there';

say %hash{"4"}:exists ?? '"4" exists in hash' !! '"4" not there';
there';

I can see that $int and $other_int look like the same object. However, the key "4" is not there even though it’s the same string as 4:

int: Int|4
other: Int|4
Same object
Other int exists in hash
"4" not there

That can be a bit weird if I wasn’t expecting that.

Consider the angle brackets version of quote words operator, <...>. This form of quote words creates allomorphs. When it sees things that look like numbers, it creates an object such as IntStr that inherits from both number and string sides. This means, though, that as an object hash key it has a very particular form. In this object hash, I make one element with the <...> quoting around the key 4. Then, I test to see if the string "4" is in the hash:

use v6;

my %hash{Any};

%hash = <4 hello>;

say %hash{"4"}:exists ?? 'Exists in hash' !! 'Not there';

And, I see that it’s not in the hash:

Not there

The allomorph version is an IntStr, but the "4" is a Str. They are not the same object, so the latter one is not a key in the hash.

That’s not a big deal if you are expecting that. But, consider a more useful object hash that only allows a certain sort of object. Maybe I want them to all be Date objects. This way, I force people to go through a central way of making keys. If they try to go around that, things don’t work:

my %hash{Date};

%hash{ Date.new(now) } = ( event => 'Something cool', rating => '6 stars' );

my $too_cool_for_your_api = '12-03-2016';

say %hash{ $too_cool_for_your_api };

I get an exception when I try to go around the constraint when I think I know better than the API:

Type check failed in binding to key; expected Date but got Str ("12-03-2016")

Perl 6 lets me force the other programmers to construct the hash keys just the way I need them.

3 thoughts on “Day 3 – Object Hashes

  1. It’s worth noting you can restrict the type of values as well. Just place the type before the variable name when declaring it:

    my Date %hash{Int}; # use Int keys and accept only Date values

    %hash{42} = 72; # Type check failed in binding to assignval; expected Date but got Int (72)

  2. I found it of interest and encouraging to expand on the Date example with another object set to the same day. It worked as I would have expected.

    my %hash{Date};

    %hash{ Date.new(‘2016-12-03’) } =
    ( event => ‘Something cool’, rating => ‘6 stars’ );

    my $same_day = Date.new(‘2016-12-03′);
    say $same_day.WHICH;
    say %hash{ $same_day }; # prints value set for Dec. 3 above

    my $too_cool_for_your_api = ’12-03-2016’;
    say %hash{ $too_cool_for_your_api };

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.