Control Flow
2026-03-11
Every program needs to make decisions and repeat actions. Raku gives you a full toolkit for control flow, from the familiar if/else to the elegant given/when pattern matching construct. Let us walk through all of them with practical examples.
if / elsif / else
The basics work just like you would expect, but Raku does not require parentheses around the condition:
my $temp = 22;
if $temp > 30 {
say "It's hot!";
}
elsif $temp > 20 {
say "It's pleasant.";
}
elsif $temp > 10 {
say "It's cool.";
}
else {
say "It's cold!";
}
Note that it is elsif, not elseif or elif. The curly braces are always required (no single-line if without braces in block form).
Truthiness
Raku considers these values to be false: False, 0, "" (empty string), Nil, () (empty list), and undefined type objects. Everything else is true.
if 0 { say "yes" } else { say "no" }
if "" { say "yes" } else { say "no" }
if "0" { say "yes" } else { say "no" }
if 1 { say "yes" } else { say "no" }
if Empty { say "yes" } else { say "no" }
Notice that "0" is true in Raku, unlike in Perl 5. A non-empty string is always true.
unless
unless is the negated form of if. It reads more naturally when you want to check that something is not true:
my $logged-in = False;
unless $logged-in {
say "Please log in first.";
}
There is no unlesss (ha) and there is no unless/else. If you need an else branch, just use if/else.
given / when
This is Raku's powerful pattern matching construct, similar to switch/case in other languages but far more flexible:
my $grade = "B";
given $grade {
when "A" { say "Excellent!" }
when "B" { say "Good job!" }
when "C" { say "Satisfactory." }
when "D" { say "Needs improvement." }
default { say "Invalid grade." }
}
The magic of given/when is that when uses smart matching (~~) under the hood. This means you can match against types, ranges, regexes, and more:
my $value = 42;
given $value {
when Int { say "It's an integer" }
when Str { say "It's a string" }
when Rat { say "It's a rational" }
default { say "Something else" }
}
my $score = 85;
given $score {
when 90..100 { say "A" }
when 80..89 { say "B" }
when 70..79 { say "C" }
when 60..69 { say "D" }
default { say "F" }
}
my $input = "hello123";
given $input {
when /^\d+$/ { say "All digits" }
when /^\w+$/ { say "Alphanumeric" }
when /\s/ { say "Contains spaces" }
default { say "Other" }
}
After a successful when block, execution automatically exits the given block (no need for break like in C). If you want fall-through behavior, use proceed:
my $n = 15;
given $n {
when * %% 3 { say "Divisible by 3"; proceed }
when * %% 5 { say "Divisible by 5"; proceed }
when * > 10 { say "Greater than 10" }
}
The %% operator means "is divisible by," and * is the Whatever star acting as a placeholder for the topic.
for Loops
The for loop iterates over lists. The loop variable is declared with ->:
for 1..5 -> $n {
say "Number: $n";
}
You can iterate over arrays, ranges, and anything that is iterable:
my @fruits = <apple banana cherry>;
for @fruits -> $fruit {
say "I like $fruit";
}
Taking Multiple Values at Once
You can grab multiple values per iteration:
for 1..6 -> $a, $b {
say "$a and $b";
}
The Topic Variable ($)
If you do not name a loop variable, Raku uses the topic variable $:
for <red green blue> {
say "Color: $_";
}
Many Raku methods work on $_ implicitly:
for <hello world> {
.say;
.uc.say;
}
loop
The loop keyword is Raku's equivalent of C's for loop:
loop (my $i = 0; $i < 5; $i++) {
say $i;
}
(each on its own line)
An infinite loop with no conditions:
loop {
say "This runs forever (until you break out)";
last;
}
while and until
while loops run as long as the condition is true:
my $count = 5;
while $count > 0 {
say $count;
$count--;
}
say "Liftoff!";
until loops run as long as the condition is false (the inverse of while):
my $count = 0;
until $count == 5 {
say $count;
$count++;
}
repeat while / repeat until
These are Raku's "do...while" loops. The body executes at least once before the condition is checked:
my $input;
repeat {
$input = prompt("Enter 'quit' to exit: ");
} while $input ne "quit";
say "Goodbye!";
my $n = 0;
repeat {
say $n;
$n++;
} until $n == 3;
Loop Control: last, next, redo
These keywords control loop execution:
- last exits the loop entirely (like
break in C)
- next skips to the next iteration (like
continue in C)
- redo restarts the current iteration without re-evaluating the condition
for 1..10 -> $n {
next if $n %% 2;
last if $n > 7;
say $n;
}
for ^3 {
my $answer = prompt("Enter a number (1-10): ");
unless $answer ~~ /^\d+$/ && 1 <= +$answer <= 10 {
say "Invalid! Try again.";
redo;
}
say "You entered: $answer";
}
Labeled Loops
For nested loops, you can use labels to control which loop last or next applies to:
OUTER: for 1..3 -> $i {
for 1..3 -> $j {
next OUTER if $j == 2;
say "$i, $j";
}
}
Statement Modifiers
Many control flow keywords can be used as postfix modifiers on a single statement. This makes short conditionals very readable:
say "It's big!" if $n > 100;
say "Still going" unless $done;
say $_ for 1..5;
.say for <alpha beta gamma>;
my $x = 42;
say "positive" if $x > 0;
say "even" if $x %% 2;
say "not zero" unless $x == 0;
Statement modifiers keep simple logic on one line while the block form handles complex logic with multiple statements.
with / without
with is like if, but it checks for definedness rather than truthiness:
my $name = "Alice";
with $name {
say "Hello, $_";
}
my $nothing;
without $nothing {
say "Value is undefined";
}
This is very useful when dealing with values that could be zero or empty string (both of which are defined but false):
my $count = 0;
if $count { say "if: yes" } else { say "if: no" }
with $count { say "with: yes" } else { say "with: no" }
Putting It All Together
my @tasks = (
{ name => "Write code", done => True },
{ name => "Run tests", done => False },
{ name => "Deploy", done => False },
{ name => "Celebrate", done => False },
);
say "=== Task Report ===";
for @tasks.kv -> $i, %task {
my $status = %task<done> ?? "DONE" !! "TODO";
say "{ $i + 1 }. [{ $status }] %task<name>";
}
my $completed = @tasks.grep({ $_<done> }).elems;
given $completed {
when 0 { say "\nNothing done yet. Get to work!" }
when @tasks.elems { say "\nAll done! Time to celebrate!" }
default { say "\n$completed of { @tasks.elems } tasks completed." }
}
What is Next?
You now have full control over the flow of your Raku programs. Next, we will look at subroutines and Raku's incredibly expressive signature system, which lets you do things like multi-dispatch and type-checked parameters right in the function definition.