Control Flow

Conditionals, loops, and exception handling.

Conditionals

If / Elsif / Else

my int $x = 10;

if ($x > 10) {
    say("Greater than 10");
} elsif ($x == 10) {
    say("Equal to 10");
} else {
    say("Less than 10");
}

Unless

unless is the opposite of if:

my int $logged_in = 0;

unless ($logged_in) {
    say("Please log in");
}

# Equivalent to:
if (!$logged_in) {
    say("Please log in");
}

Ternary Operator

my int $age = 20;
my str $status = $age >= 18 ? "adult" : "minor";
say($status);  # "adult"

Loops

While Loop

my int $i = 0;
while ($i < 5) {
    say($i);
    $i++;
}

For Loop

for (my int $i = 0; $i < 5; $i++) {
    say("i = " . $i);
}

Foreach Loop

Iterate over arrays:

my array @fruits = ["apple", "banana", "cherry"];

# With type declaration
foreach my str $fruit (@fruits) {
    say($fruit);
}

# Without type (uses existing variable)
my str $item;
foreach $item (@fruits) {
    say($item);
}

# "for" works like "foreach" with arrays
for my str $fruit (@fruits) {
    say($fruit);
}

Increment/Decrement

my int $x = 5;

$x++;        # Post-increment: returns 5, then x becomes 6
++$x;        # Pre-increment: x becomes 7, returns 7
$x--;        # Post-decrement: returns 7, then x becomes 6
--$x;        # Pre-decrement: x becomes 5, returns 5

# Often used in loops
my int $i = 0;
while ($i < 10) {
    say($i++);  # Print then increment
}

Loop Control

for (my int $i = 0; $i < 10; $i++) {
    if ($i == 3) {
        next;      # Skip to next iteration
    }
    if ($i == 7) {
        last;      # Exit loop
    }
    say($i);
}
# Output: 0, 1, 2, 4, 5, 6

Labeled Loops

Labels allow breaking out of nested loops:

OUTER: for (my int $i = 0; $i < 5; $i++) {
    INNER: for (my int $j = 0; $j < 5; $j++) {
        if ($j == 2) {
            next INNER;   # Skip to next inner iteration
        }
        if ($i == 3) {
            last OUTER;   # Exit both loops
        }
        say("i=" . $i . " j=" . $j);
    }
}

Exception Handling

Try / Catch / Throw

func divide(int $a, int $b) int {
    if ($b == 0) {
        throw "Division by zero!";
    }
    return $a / $b;
}

func main() int {
    try {
        my int $result = divide(10, 0);
        say("Result: " . $result);
    } catch ($e) {
        say("Error: " . $e);
    }
    say("Program continues...");
    return 0;
}
Exception Values The throw statement can throw any value (string, number, etc.). The catch block receives that value in its parameter.

Nested Try/Catch

try {
    try {
        throw "Inner error";
    } catch ($e) {
        say("Caught inner: " . $e);
        throw "Re-throwing";  # Re-throw or throw new
    }
} catch ($e) {
    say("Caught outer: " . $e);
}

Goto and Labels

For exceptional control flow (use sparingly):

func main() int {
    my int $i = 0;

start:
    say($i);
    $i++;
    if ($i < 5) {
        goto start;
    }

    say("Done");
    return 0;
}
Use Sparingly goto can make code hard to follow. Prefer structured control flow (loops, functions) in most cases. It's occasionally useful for cleanup code or state machines.

Pattern Matching

Use regex in conditionals:

my str $input = "hello123";

if ($input =~ /[0-9]+/) {
    say("Contains numbers");
}

if ($input !~ /^[0-9]+$/) {
    say("Not purely numeric");
}

# Case-insensitive match
if ($input =~ /HELLO/i) {
    say("Found hello (case-insensitive)");
}

Switch Statement

Use switch for multi-way branching:

my int $day = 3;

switch ($day) {
    case 1 {
        say("Monday");
    }
    case 2 {
        say("Tuesday");
    }
    case 3 {
        say("Wednesday");
    }
    default {
        say("Other day");
    }
}
No Fall-Through Unlike C/Java, each case is self-contained with braces. There's no fall-through behavior and no break statement needed.

Switch with Strings

my str $color = "red";

switch ($color) {
    case "red" {
        say("Stop");
    }
    case "yellow" {
        say("Caution");
    }
    case "green" {
        say("Go");
    }
    default {
        say("Unknown signal");
    }
}

Alternative: Hash Dispatch

For dynamic dispatch or function tables, hashes work well:

my hash %day_names = {
    "Mon" => "Monday",
    "Tue" => "Tuesday",
    "Wed" => "Wednesday"
};

my str $day = "Mon";
if (exists(%day_names, $day)) {
    say($day_names{$day});
} else {
    say("Unknown day");
}

Short-Circuit Evaluation

# && and || short-circuit
my int $x = 0;

# Second expression not evaluated if first is false
$x && say("x is true");

# Second expression not evaluated if first is true
$x || say("x is false");

# Use for default values
my str $name = "";
my str $display = $name || "Anonymous";