=begin pod

=TITLE class Hash

=SUBTITLE Mapping from strings to itemized values

    class Hash is Map { }

A Hash is a mutable mapping from keys to values (called I<dictionary>,
I<hash table> or I<map> in other programming languages). The values are
all scalar containers, which means you can assign to them.

Hashes are usually stored in variables with the percent C<%> sigil.

Hash elements are accessed by key via the C<{ }> postcircumfix operator:

    dd %*ENV{'HOME', 'PATH'};
    # OUTPUT: «("/home/camelia", "/usr/bin:/sbin:/bin")␤»

The general L<Subscript|/language/subscripts> rules apply providing shortcuts
for lists of literal strings, with and without interpolation.

    my %h = oranges => 'round', bananas => 'bendy';
    dd %h<oranges bananas>;
    # OUTPUT: «("round", "bendy")␤»
    my $fruit = 'bananas';
    dd %h«oranges $fruit»;
    # OUTPUT: «("round", "bendy")␤»

You can add new pairs simply by assigning to an unused key:

    my %h;
    %h{'new key'} = 'new value';

=head1 Hash assignment

Assigning a list of elements to a hash variable first empties the variable,
and then iterates the elements of the right-hand side. If an element is a
L<Pair>, its key is taken as a new hash key, and its value as the new hash
value for that key. Otherwise the value is coerced to L<Str> and used as a
hash key, while the next element of the list is taken as the corresponding
value.

    my %h = 'a', 'b', c => 'd', 'e', 'f';
    # same as
    my %h = a => 'b', c => 'd', e => 'f';
    # or
    my %h = <a b c d e f>;

If a L<Pair> is encountered where a value is expected, it is used as a
hash value:

    my %h = 'a', 'b' => 'c';
    say %h<a>.WHAT;             # OUTPUT: «(Pair)␤»
    say %h<a>.key;              # OUTPUT: «b␤»

If the same key appears more than once, the value associated with its last
occurrence is stored in the hash:

    my %h = a => 1, a => 2;
    say %h<a>;                  # OUTPUT: «2␤»

To assign to a variable without the C<%> sigil, you may use curly braces:

    my $h = { a => 1, b => 2 }; # *See Note*
    say $h.WHAT;                # OUTPUT: «Hash␤»
    say $h<a>;                  # OUTPUT: «1␤»

B<NOTE:> If one or more values reference the topic variable, C<$_>, the
right-hand side of the assignment will be interpreted as a L<Block|/type/Block>,
not a Hash:

    my @people = [
        { id => "1A", firstName => "Andy", lastName => "Adams" },
        { id => "2B", firstName => "Beth", lastName => "Burke" },
        # ...
    ];

    sub lookup-user (Hash $h) { #`(Do something...) $h }

    my @names = map {
        my $query = { name => "$_<firstName> $_<lastName>" };
        say $query.WHAT;       # OUTPUT: «Block␤»
        say $query<name>;      # fails
        CATCH { default { put .^name, ': ', .Str } };
        # OUTPUT: «X::AdHoc: Type Block does not support associative indexing.␤»
        lookup-user($query);   # Type check failed in binding $h; expected Hash but got Block
    }, @people;

Instead, you should either:

=begin item
1) use the C<%()> hash constructor:

=for code :skip-test
my  $query = %( name => "$_<firstName> $_<lastName>" );
say $query.WHAT;         # OUTPUT: «Hash␤»
say $query<name>;        # Andy Adams, Beth Burke, etc.
=end item

=begin item
2) assign to a Hash type (C<%>) directly:

=for code :skip-test
my  %query = name => "$_<firstName> $_<lastName>";   # No braces required
say %query.WHAT;         # OUTPUT: «Hash␤»
say %query<name>;        # Andy Adams, Beth Burke, etc.
=end item

=begin item
or 3) simply avoid the issue altogether by explicitly providing a name for
the topic variable:

    my @people = [
        { id => "1A", firstName => "Andy", lastName => "Adams" },
        { id => "2B", firstName => "Beth", lastName => "Burke" }
    ];
    sub lookup-user (Hash $h) { #`(Do something...) $h }
    my @names = map -> $person {
        my $query = { name => "$person<firstName> $person<lastName>" };
        say $query.WHAT;     # OUTPUT: «Hash␤»
        say $query<name>;    # Andy Adams, Beth Burke, etc.

       lookup-user($query);
    }, @people;
=end item

Even in the final case, however, specifying a Hash type (C<%>) is still the
most idiomatic and unambiguous solution:

    my @people = [
        { id => "1A", firstName => "Andy", lastName => "Adams" },
        { id => "2B", firstName => "Beth", lastName => "Burke" }
    ];
    sub lookup-user (Hash $h) { #`(Do something...) $h }
    my @names = @people.map: -> $person {
        lookup-user( %( name => "$person<firstName> $person<lastName>" ) );
    };

=head2 Slices

You can assign to multiple keys at the same time with a slice.

    my %h; %h<a b c> = 2 xx *; %h.perl.say;  # OUTPUT: «{:a(2), :b(2), :c(2)}␤»
    my %h; %h<a b c> = ^3;     %h.perl.say;  # OUTPUT: «{:a(0), :b(1), :c(2)}␤»

=head2 Non-string keys

By default keys in C<{ }> are forced to strings. To compose a hash with
non-string keys, use a colon prefix:

    my $when = :{ (now) => "Instant", (DateTime.now) => "DateTime" };

Note that with objects as keys, you cannot access non-string keys as strings:

    :{ -1 => 41, 0 => 42, 1 => 43 }<0>;  # Any
    :{ -1 => 41, 0 => 42, 1 => 43 }{0};  # 42

=head2 Constraint value types

Place a type object in-between the declarator and the name to constraint the type
of all values of a C<Hash>. Use a L<subset|/language/typesystem#subset> for
constraints with a where-clause.

    subset Powerful of Int where * > 9000;
    my Powerful %h{Str};
    put %h<Goku>   = 9001;
    try {
        %h<Vegeta> = 900;
        CATCH { when X::TypeCheck::Binding { .message.put } }
    }

    # OUTPUT:
    # 9001
    # Type check failed in binding assignval; expected Powerful but got Int (900)

=head1 Looping over hash keys and values

A common idiom for processing the elements in a hash is to loop over the
keys and values, for instance,

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.kv -> $vowel, $index {
      "$vowel: $index".say;
    }

gives output similar to this:

=for code :skip-test
a: 1
e: 2
o: 4
u: 5
i: 3

where we have used the C<kv> method to extract the keys and their respective
values from the hash, so that we can pass these values into the loop.

Note that the order of the keys and values printed cannot be relied upon;
the elements of a hash are not always stored the same way in memory for
different runs of the same program.  Sometimes one wishes to process the
elements sorted on, e.g. the keys of the hash.  If one wishes to print the
list of vowels in alphabetical order then one would write

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.sort(*.key)>>.kv -> ($vowel, $index) {
      "$vowel: $index".say;
    }

which prints

=for code :skip-test
a: 1
e: 2
i: 3
o: 4
u: 5

and is in alphabetical order as desired.  To achieve this result, we sorted
the hash of vowels by key (C<%vowels.sort(*.key)>) which we then ask for its
keys and values by applying the C<.kv> method to each element via the unary
C< >> > hyperoperator resulting in a L<List> of key/value lists.  To extract
the key/value the variables thus need to be wrapped in parentheses.

An alternative solution is to flatten the resulting list.  Then the key/value
pairs can be accessed in the same way as with plain C<.kv>:

    my %vowels = 'a' => 1, 'e' => 2, 'i' => 3, 'o' => 4, 'u' => 5;
    for %vowels.sort(*.key)>>.kv.flat -> $vowel, $index {
      "$vowel: $index".say;
    }

You can also loop over a C<Hash> using
L<destructuring|/type/Signature#Destructuring_Parameters>.

=head2 Object hashes and type constraints
X<|object hash>X<|:{}>

Hash keys are stored as L<Str> values in a normal hash. In many cases this is
sufficient, but sometimes you want the hash to preserve the objects you are using
as keys I<as the *exact* objects you are providing to the hash to use as keys>.
In these moments you want an object hash!

    my %intervals{Instant};
    my $first-instant = now;
    %intervals{ $first-instant } = "Our first milestone.";
    sleep 1;
    my $second-instant = now;
    %intervals{ $second-instant } = "Logging this Instant for spurious raisins.";
    for %intervals.sort -> (:$key, :$value) {
        state $last-instant //= $key;
        say "We noted '$value' at $key, with an interval of {$key - $last-instant}";
        $last-instant = $key;
    }

This example uses an object hash that only accepts keys of type L<Instant> to
implement a rudimentary, yet type-safe, logging mechanism. We utilize a named L<state|/language/variables#The_state_Declarator>
variable for keeping track of the previous C<Instant> so that we can provide an interval.

The whole point of object hashes is to keep keys as objects-in-themselves.
Currently object hashes utilize the L<WHICH|/routine/WHICH> method of an object, which returns a
unique identifier for every mutable object. This is the keystone upon which the object
identity operator (L<===>) rests. Order and containers really matter here as the order of
C<.keys> is undefined and one anonymous list is never L<===> to another.

    my %intervals{Instant};
    my $first-instant = now;
    %intervals{ $first-instant } = "Our first milestone.";
    sleep 1;
    my $second-instant = now;
    %intervals{ $second-instant } = "Logging this Instant for spurious raisins.";
    say ($first-instant, $second-instant) ~~ %intervals.keys;       # OUTPUT: «False␤»
    say ($first-instant, $second-instant) ~~ %intervals.keys.sort;  # OUTPUT: «True␤»
    say ($first-instant, $second-instant) === %intervals.keys.sort; # OUTPUT: «False␤»
    say $first-instant === %intervals.keys.sort[0];                 # OUTPUT: «True␤»

Since C<Instant> defines its own comparison methods, in our example a sort according to
L<cmp> will always provide the earliest instant object as the first element in the L<List>
it returns.

If you would like to accept any object whatsoever in your hash, you can use L<Any>!

    my %h{Any};
    %h{(now)} = "This is an Instant";
    %h{(DateTime.now)} = "This is a DateTime, which is not an Instant";
    %h{"completely different"} = "Monty Python references are neither DateTimes nor Instants";

There is a more concise syntax which uses binding.

    my %h := :{ (now) => "Instant", (DateTime.now) => "DateTime" };

The binding is necessary because an object hash is about very solid, specific objects,
which is something that binding is great at keeping track of but about which assignment doesn't
concern itself much.

=head2 In place editing of values

There may be times when you would like to modify the values of a hash while iterating over them.

    my %answers = illuminatus => 23, hitchhikers => 42;
    # OUTPUT: «hitchhikers => 42, illuminatus => 23»
    for %answers.values -> $v { $v += 10 }; # Fails
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::AdHoc: Cannot assign to a readonly variable or a value␤»

This is traditionally accomplished by sending both the key and the value as follows.

    my %answers = illuminatus => 23, hitchhikers => 42;
    for %answers.kv -> $k,$v { %answers{$k} = $v + 10 };

However, it is possible to leverage the signature of the block in order to specify that
you would like read-write access to the values.

    my %answers = illuminatus => 23, hitchhikers => 42;
    for %answers.values -> $v is rw { $v += 10 };

It is not, however, possible to do in-place editing of hash keys, even in the case of object hashes.

=head1 Methods

=head2 method classify-list

Defined as:

    multi method classify-list(&mapper, *@list, :&as --> Hash:D)
    multi method classify-list(%mapper, *@list, :&as --> Hash:D)
    multi method classify-list(@mapper, *@list, :&as --> Hash:D)

Populates a L«C<Hash>|/type/Hash by classifying the
possibly-empty C<@list> of values using the given C<mapper>, optionally
altering the values using the C<:&as> L«C<Callable>|/type/Callable». The
C<@list> cannot be lazy.

The mapper can be a L«C<Callable>|/type/Callable» that takes a single argument,
an L«C<Associative>|/type/Associative», or an L«C<Iterable>|/type/Iterable».
With L«C<Associative>|/type/Associative» and an L«C<Iterable>|/type/Iterable»
mappers, the values in the C<@list> represent the key and index of the mapper's
value respectively. A L«C<Callable>|/type/Callable» mapper will be executed
once per each item in the C<@list>, with that item as the argument and its
return value will be used as the mapper's value.

=head3 Simple Classification

In simple classification mode, each mapper's value is any non-Iterable and
represents a key to classify C<@list>'s item under:

    say % .classify-list: { $_ %% 2 ?? 'even' !! 'odd' }, ^10;
    # OUTPUT: «{even => [0 2 4 6 8], odd => [1 3 5 7 9]}␤»

    my @mapper = <zero one two three four five>;
    my %hash = foo => 'bar';
    say %hash.classify-list: @mapper, 1, 2, 3, 4, 4;
    # OUTPUT: «{foo => bar, four => [4 4], one => [1], three => [3], two => [2]}␤»

The mapper's value is used as the key of the L«C<Hash>|/type/Hash» to
which the C<@list>'s item will be L«C<push>ed|/routine/push». See
L«C<.categorize-list>|/routine/categorize-list» if you wish to classify an item
into multiple categories at once.

=head3 Multi-Level Classification

In multi-level classification mode, each mapper's value is an
L«C<Iterable>|/type/Iterable» that represents a tree of hash keys to classify
C<@list>'s item under:

    say % .classify-list: {
        [
            (.is-prime ?? 'prime' !! 'non-prime'),
            ($_ %% 2   ?? 'even'  !! 'odd'      ),
        ]
    }, ^10;
    # OUTPUT:
    # {
    #     non-prime => {
    #         even => [0 4 6 8],
    #         odd  => [1 9]
    #     },
    #     prime => {
    #         even => [2],
    #         odd  => [3 5 7]
    #     }
    # }

B<NOTE:> each of those L«C<Iterables>|/type/Iterable»
must have the same number of elements, or the method will throw an exception.
This restriction exists to avoid conflicts when the same key is a
leaf of one value's classification but a node of another value's classification.

=head3 C<:&as> value modifier

If C<:&as> L«C<Callable>|/type/Callable» argument is specified, it will be
called once per each item of C<@list>, with the value as the argument, and
its return value will be used instead of the original C<@list>'s item:

    say % .classify-list: :as{"Value is $_"}, { $_ %% 2 ?? 'even' !! 'odd' }, ^5;
    # OUTPUT (slightly altered manually, for clarity):
    # {
    #     even => ['Value is 0', 'Value is 2', 'Value is 4'],
    #     odd  => ['Value is 1', 'Value is 3']
    # }

=head2 method categorize-list

Defined as:

    multi method categorize-list(&mapper, *@list, :&as --> Hash:D)
    multi method categorize-list(%mapper, *@list, :&as --> Hash:D)
    multi method categorize-list(@mapper, *@list, :&as --> Hash:D)

Populates a L«C<Hash>|/type/Hash by classifying the
possibly-empty C<@list> of values using the given C<mapper>, optionally
altering the values using the C<:&as> L«C<Callable>|/type/Callable». The
C<@list> cannot be lazy.

The mapper can be a L«C<Callable>|/type/Callable» that takes a single argument,
an L«C<Associative>|/type/Associative», or an L«C<Iterable>|/type/Iterable».
With L«C<Associative>|/type/Associative» and an L«C<Iterable>|/type/Iterable»
mappers, the values in the C<@list> represent the key and index of the mapper's
value respectively. A L«C<Callable>|/type/Callable» mapper will be executed
once per each item in the C<@list>, with that item as the argument and its
return value will be used as the mapper's value.

=head3 Simple Categorization

The mapper's value is expected to be a possibly empty list of
non-L«C<Iterables>|/type/Iterable» that represent categories to place the value
into:

    say % .categorize-list: {
        gather {
            take 'prime'   if .is-prime;
            take 'largish' if $_ > 5;
            take $_ %% 2 ?? 'even' !! 'odd';
        }
    }, ^10;

    # OUTPUT:
    # {
    #     prime   => [2 3 5 7]
    #     even    => [0 2 4 6 8],
    #     odd     => [1 3 5 7 9],
    #     largish => [6 7 8 9],
    # }

Notice how some items, e.g. C<6> and C<7>, are present in several categories.

=head3 Multi-Level Categorization

In multi-level categorization, the categories produced by the mapper can are
L<Iterables|/type/Iterable> and categorization combines features
of C<classify|/routine/classify>, by producing nested hashes of classifications
for each category.

    say % .categorize-list: {
        [
            $_ > 5    ?? 'largish' !! 'smallish',
            .is-prime ?? 'prime'   !! 'non-prime',
        ],
    }, ^10;

    # OUTPUT:
    # {
    #     largish => {
    #         non-prime => [6 8 9],
    #         prime     => [7]
    #     },
    #     smallish => {
    #         non-prime => [0 1 4],
    #         prime     => [2 3 5]
    #     }
    # }

The mapper in above snippet produces single-item list (note the significant
trailing comma) with a two-item C<Array> in it. The first item in that array
indicates the first level of classification: the C<largish>/C<smallish>
categories the routine produces. The second item in that array indicates
further levels of classification, in our case the classification into
C<prime>/C<non-prime> inside of each category.

B<NOTE:> each of category L«C<Iterables>|/type/Iterable»
must have the same number of elements, or the method will throw an exception.
This restriction exists to avoid conflicts when the same key is a
leaf of one value's classification but a node of another value's classification.

=head3 C<:&as> value modifier

If C<:&as> L«C<Callable>|/type/Callable» argument is specified, it will be
called once per each item of C<@list>, with the value as the argument, and
its return value will be used instead of the original C<@list>'s item:

    say % .categorize-list: :as{"Value is $_"}, { $_ %% 2 ?? 'even' !! 'odd' }, ^5;
    # OUTPUT (slightly altered manually, for clarity):
    # {
    #     even => ['Value is 0', 'Value is 2', 'Value is 4'],
    #     odd  => ['Value is 1', 'Value is 3']
    # }

=head2 method push

Defined as:

    multi method push(Hash:D: *@new)

Adds the C<@new> elements to the hash with the same semantics as hash
assignment, but with three exceptions:

=item The hash isn't emptied first, i.e. old pairs are not deleted.

=item If a key already exists in the hash, and the corresponding value is an
L<Array>, the new value is pushed onto the array (instead of replacing it).

=item If a key already exists in the hash, and the corresponding value is not
an L<Array>, old and new value are both placed into an array in the place
of the old value.

Example:

    my %h  = a => 1;
    %h.push: (a => 1);                  # a => [1,1]
    %h.push: (a => 1) xx 3 ;            # a => [1,1,1,1,1]
    %h.push: (b => 3);                  # a => [1,1,1,1,1], b => 3
    %h.push('c' => 4);                  # a => [1,1,1,1,1], b => 3, c => 4
    push %h, 'd' => 5;                  # a => [1,1,1,1,1], b => 3, c => 4, d => 5

Please note that C<Pair>s or
L<colon pairs|/language/glossary#index-entry-Colon_Pair> as arguments to push
will be treated as extra named arguments and as such wont end up the C<Hash>.
The same applies to the sub C<push>.

    my %h .= push(e => 6);
    push %h, f => 7;
    say %h.perl;
    # OUTPUT: «{}␤»

Also note that push can be used as a replacement for assignment during hash
initialization very useful ways. Take for instance the case of an inverted
index:

    my %wc = 'hash' => 323, 'pair' => 322, 'pipe' => 323;
    (my %inv).push: %wc.invert.unique;
    say %inv;                           # OUTPUT: «{322 => pair, 323 => [pipe hash]}␤»

Note that such a initialization could also be written as

    my %wc = 'hash' => 323, 'pair' => 322, 'pipe' => 323;
    my %inv .= push: %wc.invert.unique;

B<Note:> Compared to L«C<append>|/routine/append», C<push> will add the given
value as is, whereas C<append> will L«C<slip>|/routine/slip» it in:

    my %ha = :a[42, ]; %ha.push: "a" => <a b c a>;
    say %ha; # OUTPUT: «{a => [42 (a b c a)]}␤»

    my %hb = :a[42, ]; %hb.append: "a" => <a b c a>;
    say %hb; # OUTPUT: «{a => [42 a b c a]}␤»

=head2 method append

Defined as:

    method append(+@values)

Append the provided Pairs or even sized list to the Hash. If a key already
exists, turn the existing value into an L<Array|/type/Array> and push new value
onto that C<Array>. Please note that you can't mix even sized lists and lists
of Pairs. Also, bare C<Pair>s or colon pairs will be treated as L<named
arguments|/type/Signature#Positional_vs._Named> to C<.append>.

    my %h = a => 1;
    %h.append('b', 2, 'c', 3);
    %h.append({d => 4});
    say %h;
    # OUTPUT: «{a => 1, b => 2, c => 3, d => 4}␤»
    %h.append('a', 2);
    # OUTPUT: «{{a => [1 2], b => 2, c => 3, d => 4}␤»

B<Note:> Compared to L«C<push>|/routine/push», C<append> will
L«C<slip>|/routine/slip» in the given value, whereas C<push> will add it as
is:

    my %hb = :a[42, ]; %hb.append: "a" => <a b c a>;
    say %hb; # OUTPUT: «{a => [42 a b c a]}␤»

    my %ha = :a[42, ]; %ha.push: "a" => <a b c a>;
    say %ha; # OUTPUT: «{a => [42 (a b c a)]}␤»

=head2 method default

Defined as:

    method default()

Returns the default value of the invocant, i.e. the value which is returned
when a non existing key is used to access an element in the C<Hash>.
Unless the C<Hash> is declared as having a default value by using the
L<is default|/routine/is default> trait the method returns the type object
C<(Any)>.

    my %h1 = 'apples' => 3, 'oranges' => 7;
    say %h1.default;                                       # OUTPUT: «(Any)␤»
    say %h1{'bananas'};                                    # OUTPUT: «(Any)␤»

    my %h2 is default(1) = 'apples' => 3, 'oranges' => 7;
    say %h2.default;                                       # OUTPUT: «1␤»
    say %h2{'apples'} + %h2{'bananas'};                    # OUTPUT: «4␤»

=head2 method keyof

Defined as:

    method keyof()

Returns the type constraint for the keys of the invocant. For
normal hashes the method returns the coercion type C<(Str(Any))>
while for L<object hashes|/type/Hash#Object_hashes_and_type_constraints>
the type used in the declaration of the C<Hash> is returned.

    my %h1 = 'apples' => 3, 'oranges' => 7;  # (no key type specified)
    say %h1.keyof;                           # OUTPUT: «(Str(Any))␤»

    my %h2{Str} = 'oranges' => 7;            # (keys must be of type Str)
    say %h2.keyof;                           # (Str)
    %h2{3} = 'apples';                       # throws exception
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::TypeCheck::Binding: Type check failed in binding to key; expected Str but got Int (3)␤»

=head2 method of

Defined as:

    method of()

Returns the type constraint for the values of the invocant. By default,
i.e. if no type constraint is given during declaration, the method
returns C<(Mu)>.

    my %h1 = 'apples' => 3, 'oranges' => 7;  # (no type constraint specified)
    say %h1.of;                              # OUTPUT: «(Mu)␤»

    my Int %h2 = 'oranges' => 7;             # (values must be of type Int)
    say %h2.of;                              # OUTPUT: «(Int)␤»

=head1 Subscript Adverbs

Some methods are implemented as adverbs on subscripts.

=head2 C<:exists>

The adverb C<:exists> returns C<Bool::True> if a key exists in the Hash. If more then one key is supplied it returns a C<List> of C<Bool>.

    my %h = a => 1, b => 2;
    say %h<a>:exists;   # OUTPUT: «True␤»
    say %h<a b>:exists; # OUTPUT: «(True True)␤»

=head2 C<:delete>

Use C<:delete> to remove a C<Pair> from the C<Hash>.

    my %h = a => 1;
    dd %h;         # Hash %h = {:a(1)}
    %h<a>:delete;
    dd %h;         # Hash %h = {}

=head2 C<:p>

The adverb C<:p> returns a C<Pair> or a List of C<Pair> instead of just the value.

    my %h = a => 1, b => 2;
    say %h<a>:p;    # OUTPUT: «a => 1␤»
    say %h<a b>:p;  # OUTPUT: «(a => 1 b=> 2)␤»

=head2 C<:v> and C<:k>

The adverbs C<:v> and C<:k> return the key or value or a list their of.

    my %h = a => 1, b => 2;
    say %h<a>:k;    # OUTPUT: «a␤»
    say %h<a b>:k;  # OUTPUT: «(a b)␤»

You can also use the adverbs without knowing anything about the hash by using
empty angle brackets in which case all the keys and values will be listed:

    my %h1 = a => 1;
    my %h2 = a => 1, b => 2;
    say %h1<>:k; # OUTPUT: «(a)␤»
    say %h1<>:v; # OUTPUT: «(1)␤»
    say %h2<>:k; # OUTPUT: «(a b)␤»
    say %h2<>:v; # OUTPUT: «(1 2)␤»

=end pod
