Conditional Branching

Ad Astra provides two conditional statements that control the flow of execution based on a conditional expression: the simple single-branching if statement and the multi-branching match statement.

If Statement

if 20 > 10 {
    // Body
}

The if-statement evaluates the provided expression to a boolean value. If the value is true, the body block will be executed; otherwise, the block will be skipped.

In Ad Astra, if statements do not have "else" branches (e.g., if foo {} else {} syntax is forbidden). For multi-branching logic, you should use match statements instead.

Match Statement

In Ad Astra, the multi-branching match statement serves the purpose of "switching" conditional branching, and it comes in two forms: a match statement with a subject and a match statement without a subject.

match subject {
    10 => {},
    20 => {},
    else => {},
}

match {
    foo > 10 => {},
    bar < 20 => {},
    else => {},
}

The body of the match statement (the code enclosed in {...} curly braces) consists of match arms. Each arm contains a testing expression specified before the => arrow and an arm body specified after the arrow.

Match arms are separated by , commas, with an optional trailing comma. If the arm's body is a code block, the comma separator can be omitted.

As the body, the user can specify either a code block or an expression, which will be interpreted as a block with a single expression statement.

The script engine executes match arms one by one in the order they are specified.

If the match statement has a subject expression, the engine tests for equality between the subject value and the arm's expression. If the statement does not have a subject, the engine interprets the arm's expression as a boolean.

Once the engine finds the first truthful arm, it executes its body and ends the match statement.

For example, an "if-else" branching could be expressed as follows:

match foo > 10 {
    true => dbg("foo is greater than 10"),
    false => dbg("foo is less than or equal to 10"),
}

The "switch" branching could be expressed as follows:

match foo {
    2 => dbg("foo is equal to 2"),
    7 => dbg("foo is equal to 7"),
    else =>  dbg("foo is neither 2 nor 7"),
}

Exhaustiveness

Exhaustiveness means that the conditional branching covers all possible conditions.

The if statement is never exhaustive unless the conditional expression is a trivial true or false literal, because this statement does not have a fallback "else" case.

The match statement can be exhaustive if it covers all possibilities. For example, if the statement has match arms covering both true and false values.

To make the match branching explicitly exhaustive, you can introduce a special fallback arm: else => {} (the "else" keyword is a built-in construct).

This fallback arm should be the last one in the list of match arms, and it will be executed as the final option if all previous conditions fail.

Variable Initialization

You can conditionally initialize a variable using an exhaustive match statement.

let x;

match foo {
    "bar" => x = 10,
    "baz" => x = 20,
    else => x = 30,
}

// Variable `x` is fully initialized here.

dbg(x);