The Perl 6 docs on variables notes that the %
sigil can be used with types that do the Associative role. It specifically mentions Pair, Hash, and Map. But, how would I get a Pair into a variable with the %
?
If I use a type constraint I get a curious error:
> my Pair %pair = Pair.new( 'a', 'b' )
Type check failed in assignment to %pair; expected Pair but got Str ("b")
in block <unit> at <unknown file> line 1
If I assign without the type constraint I get a hash:
my %pair = Pair.new: 'a', 'b'; # Hash, not Pair
Binding works:
my %pair := Pair.new: 'a', 'b'; # Pair
But if I use a type constraint, I get another curious error:
> my Pair %p2 := Pair.new: 'a', 'b';
Type check failed in binding; expected Associative[Pair] but got Pair (:a("b"))
in block <unit> at <unknown file> line 1
The same problem shows up with Bag and Set. Do it with Map and you end up with a mutable Hash.
I figure there's a variety of issues here, but perhaps the %
sigil isn't as versatile as I was led to believe.
We need to take a few steps back if we want to understand what is happening with your examples:
Variables are bound to objects. By default, sigilled variables are initially bound to assignable container objects (Scalar
for $
and &
, Array
for @
and Hash
for %
).
If you add a type to a variable declaration such as
my Int %var
or equivalently
my %var of Int
you place a restriction on the types of values this container may hold.
Variable assignment (=
) tries to put the value on the right hand side into the container bound to the variable on the left-hand side, which will fail if this type constraint isn't satisfied. By default, only &
variables come with such a constraint (cf (my &).VAR.of
vs (my %).VAR.of
).
In contrast, rebinding a variable (:=
) will replace the container object. If you want to place restrictions on which types of objects can be bound, you need is
instead if of
:
my %var is Pair;
%var := x => 1; # ok
%var := 42; # not ok
Sigilled variables imply default type contraints (none for $
, Callable
for &
, Positional
for @
and Associative
for %
). Note that this default constraint gets overriden by an explicit one, eg
my %var is Int;
%var := 42; # ok even though 42 is not Associative
Finally, note that is
doesn't merely set the type constraint, but will also bind the variable to a newly created instance of that type:
my %pair is Pair;
say %pair; # (Mu) => (Mu), ie Pair.new()
I'm not aware of any way to just do the former.
You can use %
sigiled containers to hold any value that does
the Associative
Role.
You must be careful, as you discovered, in how you declare and assign.
In your example, my Pair %pair
, you are saying to make a Hash
(not a Pair
) that can hold Pair
values (%pair.WHAT = 'Hash[Pair]'
)
Try this:
my Pair %p2 = mykey => Pair.new: 'a', 'b';
which can be nice. By constraining the type of values, you'll get an error if you say
%p2<c> = 'd';
since you're not assigning a Pair
.
Binding, as you also discovered, works the way you think it should.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With