Skip to content

Commit

Permalink
Merge pull request #1602 from W4anD0eR96/patch-4
Browse files Browse the repository at this point in the history
Try to fix intro of non-string keys of Hash
  • Loading branch information
Tyil committed Oct 12, 2017
2 parents cdf2a42 + d414052 commit cbf99c8
Showing 1 changed file with 68 additions and 63 deletions.
131 changes: 68 additions & 63 deletions doc/Type/Hash.pod6
Expand Up @@ -111,7 +111,11 @@ 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
=head2 Non-string keys (object hash)
X<|non-string keys>X<|:{}>
X<|object hash>X<|:{}>
By default keys in C<{ }> are forced to strings. To compose a hash with
non-string keys, use a colon prefix:
Expand All @@ -127,6 +131,63 @@ Note: The same pitfalls explained about C<{}> sometimes creating Block's also
apply to C<:{}>. At this time there is no C<%()> version for creating non-string
keys.
Now if you want to define a 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>,
then object hash is what you are looking for.
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: «False␤»
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 Constraint value types
Place a type object in-between the declarator and the name to constraint the type
Expand Down Expand Up @@ -204,66 +265,6 @@ pairs can be accessed in the same way as with plain C<.kv>:
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: «False␤»
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.
Expand Down Expand Up @@ -583,8 +584,8 @@ Defined as:
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.
while for L<non-string keys|/type/Hash#Non-string_keys>
hashes 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))␤»
Expand All @@ -595,6 +596,10 @@ the type used in the declaration of the C<Hash> is returned.
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: «X::TypeCheck::Binding: Type check failed in binding to key; expected Str but got Int (3)␤»
my %h3{Int}; # (this time, keys must be of type Int)
%h3{42} = 4096;
say %h3.keyof; # (Int)
=head2 method of
Defined as:
Expand Down

0 comments on commit cbf99c8

Please sign in to comment.