raku.gg / beginner

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!"; } # Output: It's pleasant.
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" } # no if "" { say "yes" } else { say "no" } # no if "0" { say "yes" } else { say "no" } # yes! ("0" is a non-empty string) if 1 { say "yes" } else { say "no" } # yes if Empty { say "yes" } else { say "no" } # 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." } } # Output: Good job!
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" } } # Output: It's an integer
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" } } # Output: B
my $input = "hello123"; given $input { when /^\d+$/ { say "All digits" } when /^\w+$/ { say "Alphanumeric" } when /\s/ { say "Contains spaces" } default { say "Other" } } # Output: Alphanumeric
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" } } # Output: Divisible by 3 # Divisible by 5 # 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"; } # Output: 1 and 2 # 3 and 4 # 5 and 6

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; # same as $_.say .uc.say; # same as $_.uc.say }

loop

The loop keyword is Raku's equivalent of C's for loop:
loop (my $i = 0; $i <; 5; $i++) { say $i; } # Output: 0 1 2 3 4 (each on its own line)
An infinite loop with no conditions:
loop { say "This runs forever (until you break out)"; last; # break out immediately }

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; # Output: 0 # 1 # 2

Loop Control: last, next, redo

These keywords control loop execution:
for 1..10 -> $n { next if $n %% 2; # skip even numbers last if $n >; 7; # stop after 7 say $n; } # Output: 1 # 3 # 5 # 7
# redo example: keep asking until valid input 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"; } } # Output: 1, 1 # 2, 1 # 3, 1

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, $_"; # $_ is set to $name } 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" } # if: no with $count { say "with: yes" } else { say "with: no" } # with: yes

Putting It All Together

#!/usr/bin/env raku 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.