The simplest flow of control is linear—one statement follows the next in a straight line to the end of the program. Since this is far too limiting for most situations, languages provide ways to alter the control flow.
Selection executes one set of actions out of many possible sets. The
selection control structures are if
,
unless
, and given
.
The if
statement checks a condition and
executes its associated block only if that condition is true. The
condition can be any expression that evaluates to a truth value.
Parentheses around the condition are optional:
if $blue { print "True Blue."; }
The if
statement can also have an unlimited number
of elsif
statements that check additional
conditions when the preceding conditions are false. The final
else
statement executes if all preceding
if
and elsif
conditions are
false:
if $blue { print "True Blue."; } elsif $green { print "Green, green, green they say..."; } else { print "Colorless green ideas sleep furiously."; }
The unless
statement is the logical opposite of
if
. Its block executes only when the tested
condition is false:
unless $fire { print "All's well."; }
There is no elsunless
statement, though
else
works with unless
.
The switch statement selects an action by
comparing a given
expression, the switch, to a
series of when
statements, the cases. When a case
matches the switch, its block is executed:
given $bugblatter { when Beast::Trall { close_eyes( ); } when 'ravenous' { toss('steak'); } when .feeding { sneak_past( ); } when /grrr+/ { cover_ears( ); } when 2 { run_between( ); } when (3..10) { run_away( ); } }
If these comparisons are starting to look familiar, they should. The
set of possible relationships between a given
and
a when
are exactly the same as the left and right
side of a smart match operator (~~
). The
given
aliases its argument to
$_
.[14]
The when
is a defaulting construct
that does an implicit smart match on $_
. The
result is the same as if you typed:
given $bugblatter { when $_ ~~ Beast::Trall { close_eyes( ); } when $_ ~~ 'ravenous' { toss('steak'); } when $_ ~~ .feeding { sneak_past( ); } when $_ ~~ /grrr+/ { cover_ears( ); } when $_ ~~ 2 { run_between( ); } when $_ ~~ (3..10) { run_away( ); } }
but much more convenient. In general, only one case is ever executed.
Each when
statement has an implicit
break
at the end. It is possible to fall through a
case and continue comparing, but since falling through is less
common, it is explicitly specified with a
continue
:
given $bugblatter { when Beast::Trall { close_eyes( ); continue; } when 'ravenous' { toss('steak'); continue; } when 'attacking' { hurl($spear, $bugblatter); continue; } when 'retreating' { toss('towel'); } }
The default
case executes its block when all other
cases fail:
given $bugblatter { when Beast::Trall { close_eyes( ); } when 'ravenous' { toss('steak'); } default { run('away'); } }
Any code within a given
will execute, but a
successful when
skips all remaining code within
the given
, not just the when
statements. This means the default
case
isn’t really necessary, because any code after the
final when
just acts like a
default
. But an explicit
default
case makes the intention of the code
clearer in the pure switch. There’s more than one
way to do it (TMTOWTDI).
given $bugblatter { print "Slowly I turn..."; when Beast::Trall { close_eyes( ); } print "Step by step..."; when 'ravenous' { toss('steak'); } print "Inch by inch..."; }
The when
statement can also appear outside a
given
. When they do, they simply smart match
against $_
. when
statements
also have a statement modifier form. It doesn’t have
an implicit break
:
print "Zaphod" when 'two heads';
Iteration
executes one set of actions multiple times. Perl 6’s
loop constructs are while
,
until
, loop
, and
for
.
The while
loop iterates as long as a condition is
true. The condition may be complex, but the result is always a single
boolean value because while
imposes boolean
context on its condition:
while $improbability > 1 { print "$improbability to 1 against and falling."; $improbability = drive_status('power_down'); }
until
is like while
but
continues looping as long as the condition is false.
In its simplest form, the
loop
construct is infinite. It will iterate until a statement within the
loop explicitly terminates it:
loop { print "One more of that Ol' Janx."; last if enough( ); }
loop
is also the counter iterator. Like
while
, it tests a condition before executing its
block each time, but it has added expression slots for initialization
and execution between iterations that make it ideal for counter
loops:
loop ( $counter = 1; $counter < 20; $counter++ ) { print "Try to count electric sheep..."; }
The for
loop is the list iterator, so it imposes
list context. It takes any list or array, or any expression that
produces a list, and loops through the list’s
elements one at a time. On each iteration, for
aliases $_
to the current loop element.[15]
This means all the
constructs that default to $_
, like
print
and when
, can default to
the loop variable:
for @useful_things { print; print " You're one hoopy frood." when 'towel'; }
The arrow operator, ->
, makes a named alias to
the current element, in addition to the $_
alias.
All aliases are lexically scoped to the block:
for %people.keys -> $name { print; # prints $_ (same as $name) print ":", %people{$name}{'age'}; }
The arrow operator also makes it possible to iterate over multiple loop elements at the same time:
for %ages.kv -> $name, $age { print $name, " is now ", $age; }
You can combine the arrow operator with the zip
function or zip operator to loop over several lists, taking some
specified number of elements from each on every iteration, as in the
following code.
# one from each array for zip(@people,@places,@things) -> $person, $place, $thing { print "Are you a $person, $place, or $thing?"; } # two from each array for zip(@animals, @things, by=>2) -> $animal1, $animal2, $thing1, $thing2 { print "The animals, they came, they came in by twosies, twosies: "; print "$animal1 and $animal2"; print "Two things. And I call them, $thing1 and $thing2."; } # two from the first array and one from the second for zip(@colors=>2, @textures=>1) -> $color1, $color2, $texture { $mix = blend($color1, $color2); draw_circle($mix, $texture); }
The
next
and last
keywords
allow you to interrupt the control flow of a loop.
next
skips the remaining code in the loop and
starts the next iteration. last
skips the
remaining code in the loop and terminates the loop:
for @useful_things -> $item { next when 'towel'; last when 'bomb'; print "Are you sure you need your $item?"; }
In Perl 6, every block is a closure, so you get consistent behavior throughout the language, whether the block is a control structure, an argument passed to a subroutine, an anonymous subref, or the definition of a named element such as a subroutine, method, or class. What is a closure? Closures are chunks of code that are tied to the lexical scope in which they’re defined. When they’re stored and later executed at some point far removed from their definition, they execute using the variables in their original scope, even if those variables are no longer accessible any other way. It’s almost as if they package up their lexical scope to make it portable.
The fact that all blocks are closures has some implications. Every
block can have arguments passed to it. This is how
for
creates a $_
alias for the
iterator variable. Every block defines a lexical scope. Every block
has the potential to be stored and executed later. Whether a block is
stored or executed immediately depends on the structure that uses it.
The control structures we’ve discussed so far all
execute their blocks where they’re defined. A bare
block executes immediately when it’s alone, but is
stored when it’s in an assignment context or passed
as a parameter:
# executed immediately { print "Zaphod"; } # stored $closure = { print "Trillian"; }
my
and
our
are different ways of declaring
variables. my
declares a variable in the current
lexical scratchpad, while our
declares a lexical
alias to a variable in the package symbol table.
my $lexical_var; our $package_var;
temp
and
let
are not declarations, they are runtime
commands to store off the current value of a variable so it can be
restored later. temp
variables always restore
their previous value on exiting the lexical scope of the
temp
, while let
variables keep
the temporary value, unless they are explicitly told to restore it:
temp $throwaway; let $hypothetical;
Every block may have a series of control flow handlers attached to it. These are called "property blocks” because they are themselves blocks (i.e., closures), attached as properties on the block. Property blocks are defined within their enclosing block by an uppercase keyword followed by a block (they’re also sometimes called NAMED blocks):
NEXT { print "Coming around again." }
Property blocks aren’t executed in sequential order
with the other code in the enclosing block—they are stored at
compile time and executed at the appropriate point in the control
flow. NEXT
executes between each iteration of a
loop, LAST
executes at the end of the final
iteration (or simply at the end of an ordinary block).
PRE
and POST
are intended for
assertion checking and cannot have any side effects.
PRE
executes before everything else in the block,
and POST
executes after everything else in the
loop. CATCH
, KEEP
, and
UNDO
are related to exception handling.
KEEP
and UNDO
are variants of
LAST
and execute after CATCH
.
KEEP
executes when the block exits with no
exceptions, or when all exceptions have been trapped and handled;
UNDO
executes when the block exits with untrapped
exceptions.
This example prints out its loop variable in the body of the block:
for 1..4 { NEXT { print " potato, "; } LAST { print "." } print; }
Between each iteration, the NEXT
block executes,
printing “potato”. At the end of
the final iteration, the LAST
block prints a
period. So the final result is “1 potato, 2 potato,
3 potato, 4”.
Property blocks are lexically scoped within their enclosing block, so they have access to lexical variables defined there.
for 5..7 -> $count { my $potato = " potato, "; NEXT { print $count, $potato; } LAST { print $count, $potato, "more."; } }
There are two types of
exceptions:
error
exceptions and control flow exceptions. All
exceptions are stored in the error object $!
.
Exceptions are classes that inherit from the
Exception
class.
Error exceptions are thrown by throw
,
die
, and fail
(under
use fatal
). Any block can be an error exception
handler. All it needs is a CATCH
block.
CATCH
blocks always topicalize
$!
, so the simplest way to test for a particular
exception is to compare it to a class name using a
when
statement.[16]
CATCH { when Err::Danger { warn "fly away home"; } }
The $!
object will also stringify to its text
message if you match it against a pattern.
CATCH { when /:w I'm sorry Dave/ { warn "HAL is in the house."; } }
If the CATCH
block is exited by an explicit
break
statement, or by an implicit
break
in a when
or
default
case, it marks the exception as clean.
Otherwise, it rethrows the exception to be caught by some outer
block.
Once an exception is thrown, execution skips straight to the
CATCH
block and the remaining code in the block is
skipped. If the block has
POST
, KEEP
, or
UNDO
property blocks, they will execute after the
CATCH
block.
Control flow exceptions handle alterations in the flow of control
that aren’t errors. When you call
next
to skip the remaining code in the loop and go
on to the next iteration, you’re actually throwing a
control exception. These exceptions are caught by the relevant
control structure: next
and
last
exceptions are caught by loops, a
return
exception is caught by a subroutine
or method, etc.
[14]
$_
is
always the current topic (think “topic of
conversation”), so the process of aliasing a
variable to $_
is known as
“topicalization.”
[15] Topicalization again.
[16] See the earlier section Section 4.2.11 for a complete set of comparison relations.
Get Perl 6 Essentials now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.