🔬52. An attempt to understand how [*] works in Perl 6

🔬52. An attempt to understand how [*] works in Raku

N. B. Perl 6 has been renamed to Raku. Click to read more.


Reduction operators are one of the many attractive features of Perl 6. A classical example is calculating factorial:

say [*] 1..5; # 120

It is remarkable that in the AST output (generated with the --target=ast command-line option) you do not see any cycles. There is the METAOP_REDUCE_LEFT call, and obviously, the rest is hidden on the deeper levels.

- QAST::Stmts 
- QAST::WVal(Array) 
- QAST::Stmts <sunk> say [*] 1..5
    - QAST::Stmt <sunk final> say [*] 1..5
    - QAST::Want <sunk>
        - QAST::Op(call &say) <sunk> :statement_id<?> say [*] 1..5
        - QAST::Op(call) <wanted> [*] 1..5
            - QAST::Op(call &METAOP_REDUCE_LEFT) <wanted>
            - QAST::Var(lexical &infix:<*>) <wanted>
            - QAST::Op(call &infix:<..>) <wanted> ..
            - QAST::Want <wanted> 1
                - QAST::WVal(Int) 
                - Ii
                - QAST::IVal(1) 
            - QAST::Want <wanted> 5
                - QAST::WVal(Int) 
                - Ii
                - QAST::IVal(5)

Nevertheless, let us at least look at the Grammar and see how it handles the reduction operator.

regex term:sym<reduce> {
    :my $*IN_REDUCE := 1;
    :my $op;
    <?before '['\S+']'>
    <!before '[' <.[ - + ? ~ ^ ]> <.[ \w $ @ ]> > # disallow accidental prefix before termish thing

    '['
    [
    || <op=.infixish('red')> <?[\]]>
    || $<triangle>=[\\]<op=.infixish('tri')> <?[\]]>
    || <!>
    ]
    ']'
    { $op := $<op> }

    <.can_meta($op, "reduce with")>

    [
    || <!{ $op<OPER><O>.made<diffy> }>
    || <?{ $op<OPER><O>.made<pasttype> eq 'chain' }>
    || { self.typed_panic: "X::Syntax::CannotMeta", meta => "reduce with", operator => ~$op<OPER><sym>, dba => ~$op<OPER><O>.made<dba>, reason => 'diffy and not chaining' }
    ]

    { $*IN_REDUCE := 0 }
    <args> 
}

The regex needs a pair of square brackets (shown in blue) and an operator between them. The operator is saved in $<op> but also in the $op local variable: notice how you can use a colon to declare variables inside the regex rules.

Then, the operator is checked if it can be reduced (.can_meta), and finally, some arguments are parsed. In our case, the <args> rule should match with 1..5.

What happens in-between with all those diffy and pasttype, is not clear for me. But notice how a dynamic variable $*IN_REDUCE is used as a flag so that inner rules understand that they are parsing something inside the reduction meta-operator.

Further adventures of the reduction story are even less clear. Let us just take a brief look at the corresponding action (actually, to its first part):

method term:sym<reduce>($/) {
    my $base := $<op>;
    my $basepast := $base.ast
        ?? $base.ast[0]
        !! QAST::Var.new(:name("&infix" ~ $*W.canonicalize_pair('', $base<OPER><sym>)),
 :scope<lexical>);
    my $metaop := baseop_reduce($base<OPER><O>.made);
    my $metapast := QAST::Op.new( :op<call>, :name($metaop), WANTED($basepast,'reduce'));
    my $t := $basepast.ann('thunky') || $base<OPER><O>.made<thunky>;
    if $<triangle> {
        $metapast.push($*W.add_constant('Int', 'int', 1));
    }
    my $args := $<args>.ast;
    # one-arg rule?
    if +$args.list == 1 && !$args[0].flat && !$args[0].named {
        make QAST::Op.new(:node($/),
                          :op<call>,
                          WANTED($metapast,'reduce/meta'),
                          WANTED($args[0],'reduce/meta'));
    }

    . . .

}

Everything ends with generating an item in QAST.

(By the way, did you know why the name starts with ‘Q’? Originally, it was PAST, short for Parrot AST. Then, a newer version of the tree appeared, and the next letter from the alphabet was used instead.)

One thought on “🔬52. An attempt to understand how [*] works in Perl 6”

Leave a Reply

Your email address will not be published. Required fields are marked *

Retype the CAPTCHA code from the image
Change the CAPTCHA codeSpeak the CAPTCHA code