beps/docs/proposals/BEP-002-match/ideas/match-syntax-forma.md
match Expression (v3)This page re-frames the match design in the Diátaxis style. The Reference section captures the normative specification: grammar, typing, evaluation, and diagnostics. The Explanation section records the rationale, constraints, and open questions that informed those choices.
This version (v3) incorporates the syntax decisions from the original proposal (v1), specifically using variable: Type for pattern matching.
match is an expression that evaluates to the value produced by the first arm whose pattern (and optional guard) succeeds._ or an unqualified binding pattern to opt in to “everything else.”return inside an arm exits the function, not just the match. To produce a value from the arm, yield an expression (single expression or the last expression of a block).match-expression ::= "match" "(" expression ")" match-block
match-block ::= "{" match-arm+ "}"
match-arm ::= pattern guard? "=>" arm-body
guard ::= "if" expression // must be bool
arm-body ::= expression | block // block is `{ ... }`
pattern ::= wildcard-pattern
| binding-pattern
| typed-binding-pattern
| literal-pattern
| enum-variant-pattern
| destructuring-pattern
wildcard-pattern ::= "_" ( ":" type-name )?
binding-pattern ::= identifier
typed-binding-pattern ::= identifier ":" type-name
literal-pattern ::= literal
enum-variant-pattern ::= type-name "." identifier
destructuring-pattern ::= type-name? "{" field-patterns? "}"
field-patterns ::= field-pattern ( "," field-pattern )*
field-pattern ::= identifier ":" pattern
| identifier
| ".." // captures remaining fields
_ or _: Type) matches any value (optionally constraining it to a type) and introduces no binding.name) matches any remaining value and binds it without narrowing its type.name: Type) checks whether the candidate value is of the named type within a union and binds it to name.Status.Active).User { name: "Admin" }) are sugar for a guard if name == "Admin". .. keeps the remaining fields untouched but does not bind them.bool. If the guard is false the arm is skipped and matching continues.T. Each pattern is checked against the current residual type (what remains uncovered by previous arms).binding: Type can only appear when Type is a constituent of the residual union. The bound identifier has type Type.name =>) do not narrow the type; the bound identifier keeps the residual type.match expression’s type is the least upper bound of all arm body types._ or unqualified binding). Missing cases produce an error listing uncovered variants/literals._: Type or binding: Type arms count as covering that constituent; destructuring of a class covers that class variant.string, int, any, user-defined any) treat a final binding or _ as sufficient coverage.User { age } if age < 18 does not cover all User, so later arms must handle the remainder).return, break, continue, and throw behave exactly as they do outside of match (i.e., return exits the enclosing function)._ => ... when appropriate.match (status) {
Status.Active => "Active"
Status.Inactive => "Inactive"
Status.Pending => "Pending"
}
match (input) {
"Special" => "special literal"
s: string => "plain string: " + s
}
// Assuming Result is a union of classes or similar structure
match (result) {
ok: ResultOk => handle_ok(ok.value)
err: ResultErr if err.retryable => retry(err)
err: ResultErr => fail(err)
}
match (user) {
User { name: "Admin" } => "Welcome, Administrator"
User { name, age } if age < 18 => "Hello, young " + name
User { name, .. } => "Hello, " + name
}
scoped-catch semantics.let result = match (...) and concise return match (...).return remains available when exiting the entire function is clearer.variable: Type (e.g., s: string).
let s: string = ...) and function arguments (arg: Type). It treats the pattern match as a "conditional declaration."Type(variable) (Rust style) was considered but rejected because it resembles constructor calls, and primitives in BAML are not wrappers.Type { field: pattern } syntax, consistent with object construction._) end the analysis... but does not yet specify binding remaining fields; decide whether rest bindings are needed.match ergonomics: consider sugar for match inside pipeline expressions.Link the original exploratory notes (match-syntax.md) here once this spec is adopted, so readers can jump between the reference and the historical context.