Table of Contents

Match forms

Use match when code should handle several possible shapes or values in one place. It is especially useful for unions, because the compiler can check whether every case is handled.

Raven supports match in expression and statement positions. The normal expression form is keyword-first so it reads the same way as statement-form match, which matches the shape most programmers expect:

  • Expression form: match scrutinee { ... }
  • Statement form: match scrutinee { ... }
  • Postfix expression form: scrutinee match { ... }

The postfix expression form remains valid for composition cases where the scrutinee is already a prefix expression, such as try Convert.ToInt32(text) match { ... }.

In statement context, match scrutinee { ... } is parsed as a MatchStatementSyntax before expression statements are considered. The keyword-first match expression form is used only when the parser is already expecting an expression, such as after =, return, an argument separator, or another expression-only position.

The parser represents these as distinct syntax nodes: keyword-first expression form produces MatchExpressionSyntax, postfix expression form produces PostfixMatchExpressionSyntax, and statement form produces MatchStatementSyntax.

Both forms use the same pattern binder and diagnostics, including exhaustiveness checking and unreachable-arm detection.

In expression form, the match result is the selected arm expression value. In statement form, the selected arm expression is evaluated and its resulting value is discarded. When statement-form match is the final statement in a value-returning body, its selected arm value contributes an implicit tail return.

Likewise, statement-form if with an else branch contributes an implicit tail return when it is the final statement in a value-returning body.

In statement form, arm block expressions are interpreted in statement context. That means explicit return/throw statements inside those arm blocks are valid. return exits the enclosing function/method and throw raises an exception. In expression form, direct arm expressions may use return and throw expressions, but statement return/throw inside block-expression arms remains disallowed and reports RAV1900/RAV1907.

Arm bodies accept any expression, including block expressions:

let label = match value {
    0 => { "zero" }
    _ => {
        let text = "other"
        text
    }
}

match value {
    0 => { Console.WriteLine("zero") }
    _ => {
        Console.WriteLine("other")
        ()
    }
}
let label = try Convert.ToInt32(text) match {
    Ok(let value) => "Parsed ${value}"
    Error(_) => "Invalid"
}

An optional outer binding keyword may appear before a match-arm pattern:

match values {
    let [first, second, ...rest] => first + second + rest.Length
    _ => 0
}

match value {
    let Some((x, y)) => x + y
    _ => 0
}

match value {
    let Some((x, y)) pair => pair.Value
    _ => 0
}

For match arms, the outer binding keyword uses the same shorthand rule as deconstruction assignment and for pattern targets: it supplies the binding mode for otherwise bare captures inside the arm pattern. The same ambient binding mode also applies to an optional trailing whole-pattern designation. Mixing an outer binding keyword with inline pattern binding keywords in the same arm is an error.

Statement-form match with block arms may use explicit return:

class Evaluator {
    func Eval(scrutinee: bool) -> bool {
        match scrutinee {
            true => {
                return true
            }
            false => {
                return false
            }
        }
    }
}