14

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.
Mark Reed
  • 91,912
  • 16
  • 138
  • 175
brian d foy
  • 129,424
  • 31
  • 207
  • 592

2 Answers2

16

Your question1 and this answer only apply to braced code ({...}) in term position2.

The Rule

If braced code does not have a signature or include top level statements, and is empty or contains just a list whose first element is a % sigil'd variable (eg %foo) or a literal pair (eg :bar), it's 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)

Otherwise it's a Block.

To force Hash, or Block, interpretation

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

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

  • To force a {...} term to construct a Block instead of a Hash, include a ;, eg {; ... }.

  • To force a {...} term to construct a Hash instead of a Block, follow the rule per summary above / details below (or write %(...) instead of braced code).

Detailed examples

The rest of this answer provides exhaustive details in case anyone thinks the relatively simple rule above is wrong.


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)

Some braced code has an implicit signature that is generated due to syntax encountered within the braces:

  • Use of a "pronoun" ($_, @_ or %_) inside braced code. This results in a Block with a signature (an implicit one if there's no explicit one). Use of @_ or %_ pronouns is always explicit; use of $_ can be explicit, or implied if there's a methodop such as .method with no left hand side argument. In other words, even { :foo, .key, .value } is a Block with a signature ((;; $_? is raw)) due to .key'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 braced code in the first line starts with :foo;; the ; means the braced code contains statements.

The second line contains multiple statements but the braced code doesn't see them as top level statements. Instead they're producing values within individual elements of a list -- which is not itself a statement.

A top level declaration of an identifier also mean it's a Block. A declaration is a statement, but I've included this bit 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 value 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 inside parentheses within the {...}. So the braced code is a Block.


As noted at the start, 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 instead:

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 code that are terms:

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

Examples of braced code 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 code that are terms. For more details about terms, or more specifically "term position" (places in the grammar where a braced code will be interpreted as a term), see the comments below this answer.

3 WAT 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.

raiph
  • 31,607
  • 3
  • 62
  • 111
  • 2
    What do you mean by "in term position"? Do we have such as thing as "positions"? If that's the case, they should be documented. – jjmerelo May 25 '19 at 05:23
  • 1
    Term position means where a `term` is acceptable to the language's grammar. As the doc I linked says, "Most syntactic constructs in Perl 6 can be categorized in terms and operators." I think it corresponds to a value, which includes an expression, which can't be further broken down into a list of values. In, er, terms of English, it's like the position where a noun or noun phrase can appear. In implementation terms I think it corresponds to `term` rules in Rakudo's Perl 6 Grammar.nqp file... – raiph May 25 '19 at 09:19
  • ...namely `token`s clumped together [here](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L1466-L1557) and [here](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3000-L3177) plus the [`regex term:sym`](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3788-L3813) and perhaps the [`token termish`](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3627-L3659). – raiph May 25 '19 at 09:19
  • 2
    None of the matches I found of "term position" in the design docs defined it. I think it is a well known term (!) in parsing in general and P5 culture in particular so Larry didn't bother to define it. This attempt to hunt it down reminded me I've researched it before... Which led me to the section **What is a term?** in [my SO answer to *“Two terms in a row” error*](https://stackoverflow.com/questions/50775130/two-terms-in-a-row-error/50779482#50779482). It's pretty lame. I suggest asking Larry. – raiph May 25 '19 at 11:10
  • 1
    For now I'm concluding that "term position" boils down to where `` gets called in Grammar.nqp. I see four such calls with the main ones being the two which appear in the `termish` token [here](https://github.com/rakudo/rakudo/blob/1f89b12ee871c6bd5d753acb916ec495d959dc46/src/Perl6/Grammar.nqp#L3636). That wraps my effort to answer your question "What do you mean by "in term position"? As for "Do we have such as thing as "positions"?", I'd say those are syntactic slots in the grammar. I reckon that's all I've got for you. Hope it helps. @jjmerelo ^^ – raiph May 25 '19 at 11:34
  • 2
    I've now created an issue for this https://github.com/Raku/doc/issues/3774 Should have done it way back then... – jjmerelo Jan 09 '21 at 08:12
14

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.

Samantha M.
  • 496
  • 3
  • 5
  • 2
    I seem to recall that creating hashes with '{ }' used to throw an error and tell you to use '%( )'. In my opinion this behavior should return. – ab5tract May 22 '17 at 11:38
  • 7
    The documentation needs a major update to reflect this, then. The `%()` is mentioned as an alternative when `{}` doesn't work. Where is the source for the preferred way to do things if it's not the docs? – brian d foy May 23 '17 at 00:25
  • 2
    I fully agree with your assessment Brian. I opened a docs issue here https://github.com/perl6/doc/issues/1380 We used to have deprecation warnings that would show after the programs completion, maybe that's something we might want to add for 6.d when you create a Hash with `{ }`. – Samantha M. Jun 15 '17 at 12:17
  • This is included now [in the style guide as a recommendation](https://github.com/perl6/doc/blob/master/STYLEGUIDE.md#prefer-the--form-of-declaring-hashes). Thanks for the issue. – jjmerelo Apr 06 '18 at 09:04