Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Perl 6 use the % sigil for something other than a Hash?

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.

like image 918
brian d foy Avatar asked May 19 '17 01:05

brian d foy


2 Answers

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.

like image 111
Christoph Avatar answered Sep 17 '22 15:09

Christoph


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.

like image 41
Curt Tilmes Avatar answered Sep 20 '22 15:09

Curt Tilmes