How does exhaustive enum switch work?

← Control Flow · Ref: Q73

When you switch over an enumeration and use direct constant references in cases, EK9's compiler verifies that ALL enumeration values are covered. Missing a value is a compile-time error.

EXHAUSTIVE ENUM SWITCH

All enum values must appear in cases:

  switch heading
    case Direction.North
      label: "going north"
    case Direction.South
      label: "going south"
    case Direction.East
      label: "going east"
    case Direction.West
      label: "going west"
    default
      label: "direction not set"

The compiler checks that North, South, East, and West are ALL present. Missing one is a compile error.

DEFAULT IS REQUIRED

Even with all enum values listed, a 'default' block is required. This handles the case where the enum variable is unset (EK9's tri-state semantics mean a variable can be present but have no value).

COMPILE-TIME SAFETY

If you later add a new value to the enum (say Direction.NorthWest), the compiler will flag every exhaustive switch that is now incomplete. This forces you to handle the new value everywhere, preventing silent bugs.

NON-EXHAUSTIVE OPT-OUT

Use the equality operator '==' to disable exhaustive checking:

  switch heading
    case == Direction.North
      label: "going north"
    default
      label: "not north"

With '== Direction.North' instead of 'Direction.North', the compiler treats this as a comparison, not an exhaustive match. You can handle only the values you care about.

MULTIPLE ENUM VALUES PER CASE

Group enum values with commas:

  switch heading
    case Direction.North, Direction.South
      axis: "vertical"
    case Direction.East, Direction.West
      axis: "horizontal"
    default
      axis: "none"

This is exhaustive: all four values are covered across the two cases.

EXPRESSION FORM

Exhaustive enum switch works in expression form:

  label <- switch heading
    <- rtn as String?
    case Direction.North
      rtn: "N"
    ...
    default
      rtn: "?"

See Q63 for basic switch. See Q68 for switch expression form. See Q69 for multiple values per case. See Q99 for creating enumerations. See Q100 for how EK9 enums differ from Java enums. See Q226 for enum bug prevention.

Example

defines module qa.flow.switch.exhaustive

  defines type

    Direction
      North,
      South,
      East,
      West

  defines function

    describeDirection() as pure
      -> heading as Direction
      <- description <- String()

      //Exhaustive: all four values must be present
      switch heading
        case Direction.North
          description: "heading north"
        case Direction.South
          description: "heading south"
        case Direction.East
          description: "heading east"
        case Direction.West
          description: "heading west"
        default
          description: "direction not set"

    describeAxis() as pure
      -> heading as Direction
      <- axis <- String()

      //Exhaustive with grouped values
      switch heading
        case Direction.North, Direction.South
          axis: "vertical"
        case Direction.East, Direction.West
          axis: "horizontal"
        default
          axis: "none"

    isNorth() as pure
      -> heading as Direction
      <- northward <- String()

      //Non-exhaustive: uses == operator to opt out
      switch heading
        case == Direction.North
          northward: "yes, heading north"
        default
          northward: "not heading north"

  defines program

    EnumSwitchDemo()
      stdout <- Stdout()

      // === EXHAUSTIVE SWITCH ===

      directions <- [Direction.North, Direction.South, Direction.East, Direction.West]
      for heading in directions
        stdout.println(describeDirection(heading))

      // === GROUPED ENUM VALUES ===

      for heading in directions
        stdout.println(`${describeDirection(heading)} is on ${describeAxis(heading)} axis`)

      // === NON-EXHAUSTIVE WITH == ===

      for heading in directions
        stdout.println(isNorth(heading))

      // === EXPRESSION FORM ===

      testHeading <- Direction.East
      symbol <- switch testHeading
        <- rtn as String?
        case Direction.North
          rtn: "N"
        case Direction.South
          rtn: "S"
        case Direction.East
          rtn: "E"
        case Direction.West
          rtn: "W"
        default
          rtn: "?"
      stdout.println(`Direction symbol: ${symbol}`)

Common mistakes

E01070 — EK9 has no break statement. Exhaustive enum switch cases are self-contained with no fallthrough. See ek9 -h E01070 for details.

Incorrect:

switch heading
        case Direction.North
          description: "heading north"
          break

Correct:

switch heading
        case Direction.North
          description: "heading north"

E01072 — EK9 has no return statement. Functions use declared return variables. The compiler verifies all paths initialise the return variable. See ek9 -h E01072 for details.

Incorrect:

describeDirection() as pure
      -> heading as Direction
      switch heading
        case Direction.North
          return "heading north"

Correct:

describeDirection() as pure
      -> heading as Direction
      <- description <- String()
Other ways to ask this
  • Does EK9 check that all enum values are handled in a switch?
  • How do I switch over an enumeration in EK9?
  • What happens if I miss an enum value in a switch?

Coming from another language?

Rust: match on enum is exhaustive, compiler error if arm missing, _ for wildcard. Kotlin: when on sealed class or enum is exhaustive when used as expression, else required. Java: switch on enum since Java 5, exhaustive checking only with switch expressions since Java 21 with sealed types. Python: match/case has no exhaustive checking for enums. Go: no enum type (iota constants), no exhaustive checking. C/C++: switch on enum has optional compiler warnings for missing cases (-Wswitch), not enforced. C#: switch on enum not exhaustive by default, Roslyn analyzers can check. Swift: switch on enum is exhaustive, compiler error if case missing, @unknown default for future values. EK9: switch on enum is exhaustive when using direct constant references, use == operator to opt out, default always required for unset handling.

Keywords: flow, compiler, branch, control, condition, cover, complete, enum, switch, migrate, enumeration, missing, check, exhaustive