Phasers

Program execution phases and corresponding phaser blocks

The lifetime (execution timeline) of a program is broken up into phases. A phaser is a block of code called during a specific execution phase.

Phasers

A phaser block is just a trait of the closure containing it, and is automatically called at the appropriate moment. These auto-called blocks are known as phasers, since they generally mark the transition from one phase of computing to another. For instance, a CHECK block is called at the end of compiling a compilation unit. Other kinds of phasers can be installed as well; these are automatically called at various times as appropriate, and some of them respond to various control exceptions and exit values.

Here is a summary:

BEGIN {...} #  * at compile time, ASAP, only ever runs once 
CHECK {...} #  * at compile time, ALAP, only ever runs once 
 INIT {...} #  * at run time, ASAP, only ever runs once 
  END {...} #  at run time, ALAP, only ever runs once 
 
ENTER {...} #  * at every block entry time, repeats on loop blocks. 
LEAVE {...} #  at every block exit time (even stack unwinds from exceptions) 
 KEEP {...} #  at every successful block exit, part of LEAVE queue 
 UNDO {...} #  at every unsuccessful block exit, part of LEAVE queue 
 
FIRST {...} #  at loop initialization time, before any ENTER 
 NEXT {...} #  at loop continuation time, before any LEAVE 
 LAST {...} #  at loop termination time, after any LEAVE 
 
  PRE {...} #  assert precondition at every block entry, before ENTER 
 POST {...} #  assert postcondition at every block exit, after LEAVE 
 
CATCH {...} #  catch exceptions, before LEAVE 
CONTROL {...} #  catch control exceptions, before LEAVE 
 
   LAST {...} #  supply tapped by whenever-block is done, runs very last 
   QUIT {...} #  catch async exceptions within a whenever-block, runs very last 
 
COMPOSE {...} #  when a role is composed into a class 
CLOSE   {...} #  appears in a supply block, called when the supply is closed 

Phasers marked with a * have a run-time value, and if evaluated earlier than their surrounding expression, they simply save their result for use in the expression later when the rest of the expression is evaluated:

my $compiletime = BEGIN { now };
our $temphandle = ENTER { maketemp() };

As with other statement prefixes, these value-producing constructs may be placed in front of either a block or a statement:

my $compiletime = BEGIN now;
our $temphandle = ENTER maketemp();

Most of these phasers will take either a block or a function reference. The statement form can be particularly useful to expose a lexically scoped declaration to the surrounding lexical scope without "trapping" it inside a block.

These declare the same variables with the same scope as the preceding example, but run the statements as a whole at the indicated time:

BEGIN my $compiletime = now;
ENTER our $temphandle = maketemp();

(Note, however, that the value of a variable calculated at compile time may not persist under run-time cloning of any surrounding closure.)

Most of the non-value-producing phasers may also be so used:

END say my $accumulator;

Note, however, that

END say my $accumulator = 0;

sets the variable to 0 at END time, since that is when the "my" declaration is actually executed. Only argumentless phasers may use the statement form. This means that CATCH and CONTROL always require a block, since they take an argument that sets $_ to the current topic, so that the innards are able to behave as a switch statement. (If bare statements were allowed, the temporary binding of $_ would leak out past the end of the CATCH or CONTROL, with unpredictable and quite possibly dire consequences. Exception handlers are supposed to reduce uncertainty, not increase it.)

Some of these phasers also have corresponding traits that can be set on variables. These have the advantage of passing the variable in question into the closure as its topic:

our $h will enter { .rememberit() } will undo { .forgetit() };

Only phasers that can occur multiple times within a block are eligible for this per-variable form.

The topic of the block outside a phaser is still available as OUTER::<$_> . Whether the return value is modifiable may be a policy of the phaser in question. In particular, the return value should not be modified within a POST phaser, but a LEAVE phaser could be more liberal.

Any phaser defined in the lexical scope of a method is a closure that closes over self as well as normal lexicals. (Or equivalently, an implementation may simply turn all such phasers into submethods whose primed invocant is the current object.)

When multiple phasers are scheduled to run at the same moment, the general tiebreaking principle is that initializing phasers execute in order declared, while finalizing phasers execute in the opposite order, because setup and teardown usually want to happen in the opposite order from each other.

Execution Order

Compilation Begins
 
  BEGIN {...} #  at compile time, ASAP, only ever runs once 
  CHECK {...} #  at compile time, ALAP, only ever runs once 
COMPOSE {...} #  when a role is composed into a class 
 
Execution Begins
 
   INIT {...} #  at run time, ASAP, only ever runs once 
 
Before block execution begins
 
    PRE {...} #  assert precondition at every block entry, before ENTER 
 
Loop execution begins
 
  FIRST {...} #  at loop initialization time, before any ENTER 
 
Block execution begins
 
  ENTER {...} #  at every block entry time, repeats on loop blocks. 
 
Exception maybe happens
 
  CATCH {...} #  catch exceptions, before LEAVE 
CONTROL {...} #  catch control exceptions, before LEAVE 
 
End of loopeither continuing or finished
 
   NEXT {...} #  at loop continuation time, before any LEAVE 
   LAST {...} #  at loop termination time, after any LEAVE 
 
End of block
 
  LEAVE {...} #  at every block exit time (even stack unwinds from exceptions) 
   KEEP {...} #  at every successful block exit, part of LEAVE queue 
   UNDO {...} #  at every unsuccessful block exit, part of LEAVE queue 
 
Postcondition for block
 
   POST {...} #  assert postcondition at every block exit, after LEAVE 
 
Async whenever-block is complete
 
   LAST {...} #  if ended normally with done, runs once after block 
   QUIT {...} #  catch async exceptions 
 
Program terminating
 
    END {...} #  at run time, ALAP, only ever runs once 

Program Execution Phasers

BEGIN

Runs at compile time, as soon as possible, only runs once.

Can have a return value that is provided even in later phases.

CHECK

Runs at compile time, As late as possible, only runs once.

Can have a return value that is provided even in later phases.

Code that is generated at run time can still fire off CHECK and INIT phasers, though of course those phasers can't do things that would require travel back in time. You need a wormhole for that.

INIT

Runs after compilation during main execution, as soon as possible, only runs once.

Can have a return value that is provided even in later phases.

When phasers are in different modules, the INIT and END phasers are treated as if declared at use time in the using module. (It is erroneous to depend on this order if the module is used more than once, however, since the phasers are only installed the first time they're noticed.)

Code that is generated at run time can still fire off CHECK and INIT phasers, though of course those phasers can't do things that would require travel back in time. You need a wormhole for that.

An INIT only runs once for all copies of a cloned closure.

END

Runs after compilation during main execution, as late as possible, only runs once.

When phasers are in different modules, the INIT and END phasers are treated as if declared at use time in the using module. (It is erroneous to depend on this order if the module is used more than once, however, since the phasers are only installed the first time they're noticed.)

CLOSE

Appears in a supply block. Called when the supply is closed.

Block Phasers

Execution in the context of a block has its own phases.

Block-leaving phasers wait until the call stack is actually unwound to run. Unwinding happens only after some exception handler decides to handle the exception that way. That is, just because an exception is thrown past a stack frame does not mean we have officially left the block yet, since the exception might be resumable. In any case, exception handlers are specified to run within the dynamic scope of the failing code, whether or not the exception is resumable. The stack is unwound and the phasers are called only if an exception is not resumed.

These can occur multiple times within the block. So they aren't really traits, exactly--they add themselves onto a list stored in the actual trait. So if you examine the ENTER trait of a block, you'll find that it's really a list of phasers rather than a single phaser.

All of these phaser blocks can see any previously declared lexical variables, even if those variables have not been elaborated yet when the closure is invoked (in which case the variables evaluate to an undefined value.)

ENTER

Runs at every block entry time, repeats on loop blocks.

Can have a return value that is provided even in later phases.

An exception thrown from an ENTER phaser will abort the ENTER queue, but one thrown from a LEAVE phaser will not.

LEAVE

Runs at every block exit time (even stack unwinds from exceptions).

So LEAVE phasers for a given block are necessarily evaluated after any CATCH and CONTROL phasers. This includes the LEAVE variants, KEEP and UNDO. POST phasers are evaluated after everything else, to guarantee that even LEAVE phasers can't violate postconditions.

An exception thrown from an ENTER phaser will abort the ENTER queue, but one thrown from a LEAVE phaser will not.

If a POST fails or any kind of LEAVE block throws an exception while the stack is unwinding, the unwinding continues and collects exceptions to be handled. When the unwinding is completed all new exceptions are thrown from that point.

sub answer() {
    LEAVE say I say after the return value.;
 
    42 # this is the return value 
}

Note: be mindful of LEAVE phasers directly in blocks of routines, as they will get executed even when an attempt to call the routine with wrong arguments is made:

sub foo (Int{
    say "Hello!";
    LEAVE say "oh noes!"
}
try foo rand# OUTPUT: «oh noes!␤» 

Although the subroutine's body did not get run, because the sub expects an Int and rand returned a Num, its block was entered and left (when param binding failed), and so the LEAVE phaser was run.

KEEP

Runs at every successful block exit, as part of the LEAVE queue (shares the same order of execution).

For phasers such as KEEP and POST that are run when exiting a scope normally, the return value (if any) from that scope is available as the current topic within the phaser.

UNDO

Runs at every unsuccessful block exit, as part of the LEAVE queue (shares the same order of execution).

PRE

Asserts a precondition at every block entry. Runs before the ENTER phase.

PRE phasers fire off before any ENTER or FIRST.

The exceptions thrown by failing PRE and POST phasers cannot be caught by a CATCH in the same block, which implies that POST phaser are not run if a PRE phaser fails.

POST

Asserts a postcondition at every block entry. Runs after the LEAVE phase.

For phasers such as KEEP and POST that are run when exiting a scope normally, the return value (if any) from that scope is available as the current topic within the phaser.

The POST block can be defined in one of two ways. Either the corresponding POST is defined as a separate phaser, in which case PRE and POST share no lexical scope. Alternately, any PRE phaser may define its corresponding POST as an embedded phaser block that closes over the lexical scope of the PRE.

If a POST fails or any kind of LEAVE block throws an exception while the stack is unwinding, the unwinding continues and collects exceptions to be handled. When the unwinding is completed all new exceptions are thrown from that point.

The exceptions thrown by failing PRE and POST phasers cannot be caught by a CATCH in the same block, which implies that POST phaser are not run if a PRE phaser fails.

Loop Phasers

FIRST, NEXT, and LAST are meaningful only within the lexical scope of a loop, and may occur only at the top level of such a loop block.

FIRST

Runs at loop initialization, before ENTER.

NEXT

Runs when loop is continued (either through next or because you got to the bottom of the loop and are looping back around), before LEAVE.

A NEXT executes only if the end of the loop block is reached normally, or an explicit next is executed. In distinction to LEAVE phasers, a NEXT phaser is not executed if the loop block is exited via any exception other than the control exception thrown by next. In particular, a last bypasses evaluation of NEXT phasers.

LAST

Runs when loop is aborted (either through last, or return, or because you got to the bottom of the loop and are done), after LEAVE.

Exception Handling Phasers

CATCH

Runs when an exception is raised by the current block, before the LEAVE phase.

CONTROL

Runs when a control exception is raised by the current block, before the LEAVE phase. It is raised by return, fail, redo, next, last, take, warn, proceed and succeed.

Asynchronous Phasers

LAST

Runs when a Supply finishes with a call to done or when a supply block exits normally. It runs completely after the whenever block it is placed within finishes.

This phaser reuses the name LAST, but works differently from the LAST loop phaser. This phaser is similar to setting the done routine while tapping a supply with tap.

QUIT

Runs when a Supply terminates early with an exception. It runs after the whenever block it is placed within finishes.

This phaser is similar to setting the quit routine while tapping a Supply with tap.

DOC phasers

DOC

The phasers BEGIN, CHECK and INIT are run only in documentation mode when prefixed with the DOC keyword. The compiler is in documentation when run with --doc.

DOC INIT { say 'init'  }  # prints 'init' at initialization time when in documentation mode.