Perl 6 has phasers, which are subroutines that run at particular times despite their spatial placement inside the the program text. As a program moves from one thing to another, it might trigger a phaser. I wanted to use a cute title like “Phasers set to stun!”, but Jonathan Worthington got there first in the 2012 Perl 6 Advent Calendar. (And, tomorrow is the first day of the 2016 Perl 6 Advent Calendar).
For instance, there’s a boundary once the compiler has compiled the program and when the main execution of the program is about to begin. Or, when the program enters or leaves a block.
Here are the NEXT
and LAST
block phasers:
for 0 .. 3 -> $item { put "$item: I'm the last statement in this block"; NEXT { say "NEXT: About to move onto next iteration" } LAST { say "LAST: I'm not running this block again" } }
The output shows that the put
outputs its message, then the NEXT
does. After all the iterations, the LAST
runs:
0: I'm the last statement in this block NEXT: About to move onto next iteration 1: I'm the last statement in this block NEXT: About to move onto next iteration 2: I'm the last statement in this block NEXT: About to move onto next iteration 3: I'm the last statement in this block NEXT: About to move onto next iteration LAST: I'm not running this block again
But, I can rearrange the statements and get the same output:
for 0 .. 3 -> $item { LAST { say "LAST: I'm not running this block again" } NEXT { say "NEXT: About to move onto next iteration" } put "$item: I'm the last statement in this block"; }
The phasers are attached to Perl 6’s block handling and aren’t really part of the statements inside the block even though that’s where I declared them.
But, since I declared the phasers inside the block, they can bind to variables in that scope. Here I can output a message after the last iteration:
for 0 .. 15 -> $item { state $count = 0; if $item.is-prime { put "$item is prime"; $count++; } LAST { say "There were $count primes" } }
That’s really cool! I like this much more than adding another, outer scope to handle the variable and the final message:
{ my $count; for 0 .. 15 -> $item { if $item.is-prime { put "$item is prime"; $count++; } } say "There were $count primes"; }
Note that even though the phasers don’t particularly care about their textual position, the compiler needs to have already seen anything you want the phaser to reference. This won’t work because $count
doesn’t exist yet:
for 0 .. 15 -> $item { LAST { say "There were $count primes" } # $count not compiled yet state $count = 0; if $item.is-prime { put "$item is prime"; $count++; } }
Here’s a program the shows many of the phasers:
use v6; # Program execution BEGIN { put "BEGIN, at compile time as soon as possible" } CHECK { put "CHECK, at compile time, as late as possible" } INIT { put "INIT, during main execution, as soon as possible" } END { put "END, during main execution, as late as possible" } for 0 .. 3 -> $item { my Int $square = $item ** 2; # Block phasers ENTER { say "\tENTER block" } LEAVE { say "\tLEAVE block" } KEEP { say "KEEP block: Got value $_" } # not implemented? UNDO { say "\t\tUNDO block" } PRE { say "PRE block ------" } # before running block POST { say "POST block" } # after running block, before leaving # Loop phasers FIRST { say "\tFIRST loop" } # first one when beginning looping NEXT { say "\tNEXT loop" } # last one while looping LAST { say "\tLAST loop"; say "**** LOOP is done ****" } # last one when done looping }
brian,
The KEEP and UNDO phasers are both implemented, but seemingly not in the way the docs describe. Problems: a) The docs say that KEEP receives any “return” value of the block as the topical variable $_, but it doesn’t seem to; and b) What you notice above: KEEP seems silent when it would be expected to show up.
However, I’ve found that KEEP and UNDO have complementary behaviors depending on where in the block that “return” value appears. Try the mod below of your script. This changes two things: it changes $_ to $square inside KEEP’s block and also puts it into UNDO’s, because any phaser can access objects in its block scope; and it moves the block’s actual action, so that while declaring $square at the start of the block, the statement that assigns $item ** 2 to $square comes AFTER all of the phasers.
Run the script below, and KEEP appears. Then move the $square = $item ** 2 statement to above ANY of the phasers and see UNDO appear instead. (Perl 6.c Rakudo 2016.10 on MoarVM, macOS 10.12.1)
I have not been able to figure out how this behavior fits the spec as described in the docs at .
##
use v6;
# Program execution
BEGIN { put “BEGIN, at compile time as soon as possible” }
CHECK { put “CHECK, at compile time, as late as possible” }
INIT { put “INIT, during main execution, as soon as possible” }
END { put “END, during main execution, as late as possible\n—\n” }
for 0 .. 3 -> $item {
my Int $square;
# Block phasers
ENTER { say “\tENTER block” }
KEEP { say “\t\tKEEP block: Got value $square” } # not implemented?
LEAVE { say “\tLEAVE block” }
UNDO { say “\t\tUNDO block with value $square” }
PRE { say “PRE block ——” } # before running block
POST { say “POST block” } # after running block, before leaving
# Loop phasers
FIRST { say “\tFIRST loop” } # first one when beginning looping
NEXT { say “\tNEXT loop” } # last one while looping
LAST { say “\tLAST loop”; say “**** LOOP is done ****” } # last one when done looping
$square = $item ** 2;
}
##