What does the dot before a postfix or postcircumfix in Perl 6 mean?
Asked Answered
C

2

15

In the Perl doc, there is a section about .postfix/.postcircumfix, it says that

In most cases, a dot may be placed before a postfix or postcircumfix:

my @a;
@a[1, 2, 3];
@a.[1, 2, 3]; # Same

Technically, not a real operator; it's syntax special-cased in the compiler.

I tried myself:

> my @a = 1,2,3,4,5
> @a[1]  # 2
> @a.[1] # 2

> my %a = Perl => 6, Python => 3, PHP => 7
> %a<Perl>  #6
> %a.<Perl> #6

> my @weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
> @weekdays.antipairs.hash{'Sunday'}  # 6, I expected it to be syntax wrong, but it did work!
> @weekdays.antipairs.hash.{'Sunday'} # 6, seems visual clarity or brevity

So, what does the dot before a postfix or postcircumfix in Perl 6 mean? How on earth did it do that? I'm curious about that. Thanks.

Competency answered 5/5, 2018 at 12:2 Comment(0)
P
16

An expression in Perl 6 is parsed as termish things with infixish things between them. A termish is in turn defined as zero or more prefixish things, followed by the term itself, followed by zero or more postfixish things. The postfixish takes in all of:

  • Method calls ( like .foo)
  • Postfix operators (like ++)
  • Postcircumfix operators (like the [42] in @a[42])
  • Doing hyper (>>) on these to distribute them across a data structure

Since it just looks for zero or more of them, then you can freely interleave method calls and hash and array indexes. This explains why @weekdays.antipairs.hash{'Sunday'} parses fine (and why @weekdays.antipairs.hash<Sunday> would also work, and even @weekdays.antipairs.hash<Sunday>.say is fine too).

As for the ., the grammar simply accepts and ignores a . before a postfix or a postcircumfix. Here's a slightly cut-down version of the parser, that I've annotated a bit to explain what the pieces are.

token postfixish {
    <!stdstopper>

    # If we're not in a string interpolation, allow unspace (that's
    # where you write `\      ++`, for example, allowing spreading
    # postfixish things over multiple lines).
    [ <!{ $*QSIGIL }> [ <.unsp> | '\\' ] ]?

    # Here we match the >> for doing a hyper. Note that it accepts
    # but disregards a '.' before it. It's not captured at all and
    # doesn't affect the code that is compiled.
    [ ['.' <.unsp>?]? <postfix_prefix_meta_operator> <.unsp>?]**0..1

    [
    | <OPER=postfix>
    # When there'd be no confusion with a method call, a '.' is
    # also accepted and disregarded before a postfix operator
    | '.' <?before \W> <OPER=postfix>  ## dotted form of postfix operator (non-wordy only)
    | <OPER=postcircumfix>
    # Ditto here for recognized postcircumfixes
    | '.' <?[ [ { < ]> <OPER=postcircumfix>
    | <OPER=dotty>
    | <OPER=privop>
    ]
}

Thus the . means nothing at all in this context. The parser simply accepts it and then moves on to look for the thing it actually cares about at that point.

One other thing worth noting is that postfix and postcircumfix operators all work after a dotty term too, operating on $_. Thus:

my @xs = 1..10;
.++ for @xs;
say @xs;

Will produce:

[2 3 4 5 6 7 8 9 10 11]

Here's a postcircumfix example:

my %h = a => 1, b => 2, c => 3;
say .<a> + .<c> given %h;

Which produces 4.

Perl 6 syntax is generally designed to make it easy to shift code from one form to another, and accepting the . in places even where it's not strictly needed eases that a little (so one could refactor to say %h1.<a> + %h2.<b>; and the parser will be fine with it). It may also be helpful for learning purposes to consider %h<a> short for %h.<a>, which would in turn make .<a> given %h a little less of a surprise.

The extra space afforded by the . may also aid clarity, especially if one was to stack multiple postfixes, and could even - should a language extension for some reason decide to define some "interesting" terms or postfix operators - serve as a means to disambiguate.

Picro answered 5/5, 2018 at 12:48 Comment(1)
Your explain is very clear. So it was deliberately designed. Cool,Thanks!Competency
L
2

It actually does matter when you have a sub that returns a Callable.

sub foo { 'hello' }
sub bar { &foo; }
bar.WHAT # (Sub)
bar().WHAT # (Sub)
bar.().WHAT # (Str)
bar()().WHAT # (Str)
bar().().WHAT # (Str)
bar.().().WHAT # No such method 'CALL-ME' for invocant of type 'Str'
Lariat answered 6/12, 2018 at 23:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.