Skip to main content

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 match has 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 try turns it into none.
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) and none match an optional.
  • A path with an optional argument list matches a variant constructor or an enum member, qualified as Angle.Radians(r) or bare as Radians(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 Type and pat : Type both 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";
};
Arms Match Shape, Not a Condition

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.

  • bit is covered by true and false, an enum by all its members, a variant by all its constructors, and Optional<T> by some(_) and none.
  • An extensible struct 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;
};
  • variant — the constructors a variant pattern names.
  • optional — the some/none pair a match opens.
  • enum — the members an enum match covers.
  • if — the choice between two branches, with no pattern.