Statement prefixes

Prefixes that alter the behavior of a statement or a set of them

Statement prefixes are written in front of a statement, and change their meaning, their output, or the moment they are going to be run. Since they have a specific behavior, they are also sometimes specific to some statement or group of statements.

lazy

As a statement prefix, lazy acts in front of any statement, including for loops, saving the execution for when the variable they are assigned to is actually needed.

my $incremented = 0;
my $var = lazy for <1 2 3 4> -> $d {
    $incremented++
};
say $incremented# OUTPUT: «0␤» 
say eager $var;   # OUTPUT: «(0 1 2 3)␤» 
say $incremented# OUTPUT: «4␤» 

The $incremented variable is only incremented, that is, the internal part of the loop is only run when we eagerly evaluate the variable $var that contains the lazy loop. Eagerness can be applied on a variable in other ways, such as calling the .eager method on it.

my @array = lazy { (^3).map*² )  };
say @array;       # OUTPUT: «[...]» 
say @array.eager# OUTPUT: «[0 1 4]␤» 

This prefix can also be used in front of gather to make the inner statements behave lazily; in general, any set of statements that returns a value will be made lazy using this.

eager

The eager statement prefix will eagerly return the result of the statements behind, throwing away laziness and returning the result.

my $result := eager gather { for 1..3 { say "Hey"take $_² } };
say $result[0]; # OUTPUT: «Hey␤Hey␤Hey␤1␤» 

gather is implicitly lazy when bound to a scalar. However, with eager as a statement prefix it will run all three iterations in the loop, as shown by the printed "Hey", even if we are just requesting the first one in a row.

hyper, race

A for loop will automatically serialize any HyperSeq or RaceSeq used in it; on the other hand hyper and race use (maybe simultaneous) threads to run different iterations in a loop:

my @a = hyper for ^100_000 { .is-prime }

This code is around 3x faster than the bare for. But there are a couple of caveats here:

Main difference between hyper and race is the ordering of results. Use hyper if you need the loop results to be produced in order, race if you don't care.

quietly

As a statement prefix, quietly suppresses all warnings produced by the statement it precedes.

sub marine() {};
quietly say ~&marine# OUTPUT: «marine␤» 

Calling .Str on code produces a warning. Preceding the statement with quietly will just produce the output, the name of the routine.

try

If you use try in front of a statement, it will contain the exception produced in it and store it in the $! variable, just like when it's used in front of a block.

try [].pop;
say $!# OUTPUT: «Cannot pop from an empty Array␤..» 

do

do can be used as an statement prefix to disambiguate the statement they precede; this is needed, for instance, if you want to assign the result of a for statement. A bare for will fail, but this will work:

my $counter = 0;
my $result = do for ^5 { $counter++ };
say $counter# OUTPUT: «5␤» 
say $result;  # OUTPUT: «(0 1 2 3 4)␤» 

do is equivalent, as in other cases, to surrounding a statement with a parenthesis. It can be used as an alternative with a (possibly more) straightforward syntax.

sink

As in the case of the routine, sink will run the statement, throwing away the result. Use it in case you want to run some statement for the side effects it produces.

my $counter = 0;
my $result = sink for ^5 { $counter++ };
say $counter#  OUTPUT: «5␤» 
say $result;  #  OUTPUT: «(Any)␤» 

The sink statement prefix will also convert Failures into exceptions:

sub find-the-number ( Int $n where $n < 10 ) {
    if $n == 7 {
        return True;
    } else {
        fail "Not that number" ;
    }
}
for 1..^10 {
    try {
        sink find-the-number($_);
    };
    say "Found $_" unless $!;
}

In this case, we will know that the number has been found only when the try block is not catching an exception.

once

Within a loop, runs the prefixed statement only once.

my $counter;
my $result = do for ^5 { once $counter = 0$counter++ };
say $result# OUTPUT: «(0 1 2 3 4)␤» 

gather

gather can be used in front of a statement, receiving and gathering in a list all data structures emitted from a take run anywhere from that statement:

proto sub factInt ) {*}
multi sub fact1 --> 1 ) {}
multi sub fact$x ) { take $x * fact$x-1 ) }
 
my @factors = gather say fact(13); # OUTPUT: «6227020800» 
say @factors;
# OUTPUT: «[2 6 24 120 720 5040 40320 362880 3628800 ...]» 

In this example, gather precedes say, which prints the first result of the factorial; at the same time, it's harvesting the result from every call to fact, which goes to @factor.

start

As a statement prefix, start behaves in the same way as in front of a block, that is, it runs the statement asynchronously, and returns a promise.

proto sub factInt ) {*}
multi sub fact1 --> 1 ) {}
multi sub fact$x ) {  $x * fact$x-1 ) }
 
my @promises = gather {
    for <3 4> {
        take start fact10 ** $_ );
    }
}
 
say await @promises;

The Promises created by start are gathered in an array, which returns the result of the operation once the promises have been fulfilled.

react

react can be used in concurrent programs to create blocks of code that run whenever some event occurs. It works with blocks, and also as a statement prefix.

my Channel $KXGA .= new;
for ^100 {
    $KXGA.send( (100000..200000).pick );
}
 
my @sums = ( start react whenever $KXGA -> $number {
    say "In thread "$*THREAD.id;
    say "→ ", (^$number).sum;
} ) for ^10;
 
start { sleep 10$KXGA.close(); }
 
await @sums;

In this case react prefixes whenever, which makes a long sum with every number read from a channel.

supply

The keyword supply creates on-demand supplies that you can tap. It pairs with emit, which can be used anywhere from within a supply prefixed statement.

my &cards = ->  {
    my @cards = 1..10 X~ <♠ ♥ ♦ ♣>;
    emit($_for @cards.pick(@cards.elems);
}
my $supply = supply cards;
 
$supply.tap-> $v { say "Drawing: $v" });
$supply.tap-> $v { say "Drawing: $v" }done => { say "No more cards" });
# OUTPUT: 
# [...] 
# Drawing: 1♥ 
# Drawing: 7♥ 
# Drawing: 9♥ 
# No more cards 

In this example, supply acts as prefix of the previously defined cards routine. It would very well be defined as a block, but giving it a name in this case might increase legibility or simply give the responsibility of defining it to other module.