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