match
A test of one value against ordered arms, where the first pattern that matches decides the result. It branches and destructures in one form.
variant Angle {
DMS(int32, int32, int32);
Radians(float64);
}
function whole_degrees(a: Angle) -> int32 = match (a) {
Angle.DMS(deg, min, sec) => deg;
Angle.Radians(rad) => 0;
};Arms
An arm is a pattern, =>, and a body. The body is either an expression closed by a semicolon or a
bare block, which needs no semicolon after it.
- A
matchhas at least one arm, and arms are tried in order. - Every arm body has the same type, and that type is the match's type.
- No arm matching at runtime is a runtime failure, so
tryturns it intonone.
variant Angle {
DMS(int32, int32, int32);
Radians(float64);
}
function whole_degrees(a: Angle) -> int32 = match (a) {
Angle.DMS(deg, min, sec) => deg;
Angle.Radians(rad) => 0;
};
function unwrap_or(v: Optional<int32>, fallback: int32) -> int32 = match (v) {
some(x) => x;
none => fallback;
};
whole_degrees(Angle.DMS(45, 30, 0)) // => 45
unwrap_or(some((7: int32)), 0) // => 7
unwrap_or(none, 0) // => 0
match (true) { true => "on"; false => "off"; } // => "on"Patterns
A pattern either tests the value's shape or binds it, and the two compose.
_matches anything and binds nothing. It may appear more than once.- A literal matches that value.
some(p)andnonematch an optional.- A path with an optional argument list matches a variant constructor or an enum member, qualified as
Angle.Radians(r)or bare asRadians(r). - A parenthesized list matches a tuple position-wise, and a bracketed list matches an array.
- A bare name binds whatever it is tested against.
pat as Typeandpat : Typeboth ascribe a full type, acting as a downcast that matches only values of that type.- A pattern's bindings are in scope in that arm's body and nowhere else, and they may not shadow a name from an enclosing scope.
extensible struct Contact {
contact_id: uint32;
}
struct Radar extends Contact {
scan_period_ms: uint32;
}
function describe(c: Contact) -> string = match (c) {
r as Radar => "radar";
_ as Contact => "other";
};A pattern says what the value looks like and nothing more, so some(x) if x > 0 => is not something
Flex accepts. To narrow past the shape, match the shape and then test the condition with an
if in the arm's body.
Exhaustiveness
A match that does not cover its discriminee is reported as a warning. The program still checks, and
the checker does not reject it.
bitis covered bytrueandfalse, an enum by all its members, a variant by all its constructors, andOptional<T>bysome(_)andnone.- An
extensiblestruct needs a wildcard arm, because a subtype may be declared in another file. - Numbers, strings, arrays, tuples, and non-extensible structs need a catch-all, since enumerating their values is impractical.
- A tuple is also covered by a tuple pattern whose every element pattern is itself irrefutable, as
(a, b)is.
Errors
Arms whose bodies have different types
function label(kind: uint8) -> string = match (kind) {
0 => "air";
_ => kind; // error: incompatible-branch-types
};A pattern the discriminee's type cannot match
A pattern's shape has to be one the value could have: a tuple pattern needs the right arity, a variant pattern the right constructor, a struct ascription a type in the discriminee's hierarchy.
function latitude(fix: (float64, float64)) -> float64 = match (fix) {
(lat, lon, alt) => lat; // error: incompatible-pattern
_ => 0.0;
};A discriminee the arms do not cover
variant Angle {
DMS(int32, int32, int32);
Radians(float64);
}
function whole_degrees(a: Angle) -> int32 = match (a) { // warning: non-exhaustive-match
Angle.DMS(deg, min, sec) => deg;
};