Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is that a Perl 6 Hash or Block?

Tags:

hash

block

raku

This is a bit of unexpected behavior that's likely to bite beginners. First, is this intended? Second, what other things does Raku use to guess which object to create? Does it start off thinking it's Block or Hash and change later, or does it decide on the end?

You can construct a Hash with braces and the fat arrow:

my $color-name-to-rgb = {
    'red' => 'FF0000',
    };

put $color-name-to-rgb.^name;  # Hash

Using the other Pair notation creates a Hash too.

my $color-name-to-rgb = {
    :red('FF0000'),
    };

But, absent the fat arrow, I get a Block instead:

my $color-name-to-rgb = {
    'red', 'FF0000',
    };

put $color-name-to-rgb.^name;  # Block

The Hash docs only mention that using $_ inside the braces creates a Block.

There are other ways to define a hash, but I'm asking about this particular bit of syntax and not looking for the workarounds I already know about.

$ perl6 -v
This is Rakudo version 2017.04.3 built on MoarVM version 2017.04-53-g66c6dda
implementing Perl 6.c.
like image 269
brian d foy Avatar asked May 21 '17 20:05

brian d foy


Video Answer


2 Answers

When it's a Hash

Your question1 and this answer only apply to braced blocks in term position2.

Braced code that precisely follows the rule explained below constructs a Hash:

say WHAT {                  }             # (Hash)
say WHAT { %foo             }             # (Hash)
say WHAT { %foo, ...        }             # (Hash)
say WHAT { foo => 42, ...   }             # (Hash)
say WHAT { :foo, ...        }             # (Hash)
say WHAT { key => $foo, ... }             # (Hash)

The rule

If the block is empty, or contains just a list whose first element is a % sigil'd variable (eg %foo) or a literal pair (eg :bar), and it does not have a signature or include top level statements, it's a Hash. Otherwise it's a Block.

To force Block or Hash interpretation

  • To force a {...} term to construct a Block instead of a Hash, write a ; at the start i.e. { ; ... }.

  • To write an empty Block term, write {;}.

  • To write an empty Hash term, write {}.

  • To force a {...} term to construct a Hash instead of a Block, follow the rule (explained in detail in the rest of this answer), or write %(...) instead.

An explicit signature means it's a Block

Some braced code has an explicit signature, i.e. it has explicit parameters such as $foo below. It always constructs a Block no matter what's inside the braces:

say WHAT         { key => $foo, 'a', 'b' } # (Hash)
say WHAT -> $foo { key => $foo, 'a', 'b' } # (Block)

An implicit signature also means it's a Block

Some braced code has an implicit signature that is generated due to some explicit choice of coding within the block:

  • Use of a "pronoun" inside {...} means it's a Block with a signature (an implicit signature if it doesn't already have an explicit one). The pronouns are $_, @_, and %_.

  • This includes implied use of $_ inside {...} due to a .method call with no left hand side argument. In other words, even { .foo } has a signature ((;; $_? is raw)) due to .foo's lack of a left hand side argument.

  • Use of a "placeholder" variable (e.g. $^foo).

As with an explicit signature, if braced code has an implicit signature then it always constructs a Block no matter what's inside the braces:

say WHAT { key => $_ }                     # (Block)
say WHAT { key => 'value', .foo, .bar }    # (Block)

Top level statements mean it's a Block

say WHAT { :foo; (do 'a'), (do 'b') }     # (Block)
say WHAT { :foo, (do 'a'), (do 'b') }     # (Hash)

The second line contains multiple statements but they're producing values within individual elements of a list that's the single top level expression.

A top level declaration of an identifier mean it's a Block

A declaration is a statement, but I've included this section just in case someone doesn't realize that.

say WHAT { :foo, $baz, {my $bar} }        # (Hash)
say WHAT { :foo, $baz, (my $bar) }        # (Block)

The first line contains a Block as a key that contains a declaration (my $bar). But that declaration belongs to the inner {my $bar} Block, not the outer {...}. So the inner Block is just a value as far as the outer {...} is concerned, and thus that outer braced code is still interpreted as a Hash.

In contrast the second line declares a variable directly within the outer {...}. So it's a Block.

Still Blocks, not Hashs

Recall that, to be a Hash, the content of braced code must be a list that begins with either a % sigil'd variable or a literal pair. So these all produce Blocks:

my $bar = key => 'value';
say WHAT { $bar, %baz }                   # (Block)
say WHAT { |%baz      }                   # (Block)
say WHAT { %@quux     }                   # (Block)
say WHAT { 'a', 'b', key => $foo }        # (Block)
say WHAT { Pair.new: 'key', $foo }        # (Block)

Footnotes

1 This "Hash or Block?" question is an example of DWIM design. In Raku culture, good DWIM design is considered a good thing. But every DWIM comes with corresponding WATs3. The key to good DWIM design is ensuring that, in general, WATs' barks are worse than their bites4; and that the barks are useful5; and that the net benefits of the DWIM are considered to far outweigh all the barking and biting.6

2 A term is Raku's analog of a noun or noun phrase in English. It's a value.

Examples of braced blocks that are terms:

.say given { ... }  # closure? hash?
say 42, { ... }     # closure? hash?

Examples of braced blocks that are not terms:

if True { ... }     # always a closure
class foo { ... }   # always a package
put bar{ ... }      # always a hash index

This answer only discusses braced blocks that are terms. For more details about terms, or more specifically "term position" (places in the grammar where a braced block will be interpreted as a term), see the comments below this answer.

3WAT refers to a dev's incredulous surprise when something seems crazy to them. It's known that, even for well designed DWIMs, for each one that works for most folk, most of the time, there are inevitably one or more related WATs that surprise some folk, some of the time, including some of the same folk who at other times benefit from the DWIM.

4 The bite of the WATs related to this DWIM varies. It's typically a bark (error message) that makes the problem obvious. But it can also be much more obscure:

say { a => 42 }() ;  # No such method 'CALL-ME' for invocant of type 'Hash'   WAT? Oh.
say { a => $_ }<a> ; # Type Block does not support associative indexing.      WAT? Oh.

say { a => $_, b => 42, c => 99 } .elems  # 1                                 WAT?????

5 A "bark" is an error message or warning in documentation. These can often be improved. cf Lock.protect({}) fails, but with surprising message.

6 Community member opinions differ on whether DWIM design in general, or any given DWIM in particular, is worth it. cf my perspective vs Sam's answer to this question.

like image 133
raiph Avatar answered Oct 10 '22 00:10

raiph


The preferred Perl6 way is to use %( ) to create hashes.

my $color-name-to-rgb = %(
    'red', 'FF0000',
    );

I would not recommend people use braces to create hashes, ever. If they want to make a hash then %( ) is the proper way to do it.

If you are coming from the Perl 5 world it's best to just get in the habit of using %( ) instead of { } when creating a Hash.

like image 14
Samantha M. Avatar answered Oct 10 '22 01:10

Samantha M.