7

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.

brian d foy
  • 129,424
  • 31
  • 207
  • 592

3 Answers3

9

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.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • 3
    'Sigilled variables imply default type contraints ... this default constraint gets overriden by an explicit one, eg `my %var is Int;`' Presumably this is a bug, i.e. P6 should reject binding a value that does not do Associative to a `%foo` identifier. – raiph May 20 '17 at 17:15
3

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.

brian d foy
  • 129,424
  • 31
  • 207
  • 592
Curt Tilmes
  • 3,035
  • 1
  • 12
  • 24
  • Honestly, you can just use `$` for everything and ignore `%` and `@` if you like. The best use of them IMHO is in Signatures -- they make it trivial to constrain the type of things that can get passed in to something that `does` `Associative` or `does` `Positional` without overly restricting them to, say `Array` or `Hash`, since someone might want to use other fancy or even homegrown types, but you really only care about the interface to the class--the `Role`--not precisely which type it is. – Curt Tilmes May 19 '17 at 02:29
  • Really? Not close? The binding will work the way you think it should, and I thought I explained pretty clearly your misconceptions in the OP. The comment also explains how the sigils can be very useful in Signatures. What isn't clear? – Curt Tilmes May 19 '17 at 15:21
  • Right, I pointed out that your example was making hashes that hold pairs--not what you wanted--and that binding worked, as you discovered. Signatures are also a good way to make variables that can receive any type that does the Role, exactly what you asked. – Curt Tilmes May 19 '17 at 19:56
  • My apologies if you didn't find it useful. It is just another example of the versatility and usefulness of the `%` sigil others finding this page might find helpful. – Curt Tilmes May 19 '17 at 20:20
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/144694/discussion-between-curt-tilmes-and-brian-d-foy). – Curt Tilmes May 19 '17 at 20:53
1

Honestly, this feels like a bug to me. % containers have no problem storing a Pair that is constructed via the fat arrow infix constructor:

my %pair = what => 'a pair';

The container overrides the type though, so you get a Hash rather than a Pair.

%pair.WHAT; #=> (Hash)
ab5tract
  • 1,098
  • 8
  • 7