Quick Tip #15: Phasers

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
	}

One comment

  1. 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;
    }
    ##

Leave a Reply to Bruce Van Allen Cancel reply

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