How do guard variables work in switch statements?

← Control Flow · Ref: Q75

Guard variables in switch combine variable creation with an isSet check before the switch body executes. If the guard is unset, the entire switch (including default) is skipped.

BASIC SWITCH GUARD

Use the declaration operator (<-) with 'then' or 'with' to link the guard to the switch control:

  switch value <- getResult() then value
    case 1
      stdout.println("One")
    case 2
      stdout.println("Two")
    default
      stdout.println("Other")

If getResult() is unset, nothing executes. No exception.

SWITCH EXPRESSION WITH GUARD

Switch expressions return a value. The guard protects the entire expression:

  label <- switch priority <- getPriority() then priority
    <- rtn as String: "Unknown"
    case 1
      rtn: "Low"
    case 2
      rtn: "Medium"
    case 3
      rtn: "High"
    default
      rtn: "Other"

If the guard is unset, the expression evaluates to the default return value ("Unknown").

KEYWORD INTERCHANGEABILITY

'with' and 'then' are synonyms. Use whichever reads better:

  switch value <- getResult() with value
  switch value <- getResult() then value

Both are identical in behavior.

GIVEN/WHEN ALTERNATIVE

EK9 allows 'given' as a synonym for 'switch':

  given value <- getResult() then value
    when 1
      stdout.println("One")

See Q72 for more on given/when.

See Q63 for basic switch without guards. See Q68 for switch as expression. See Q74 for guards in if statements. See Q76 for guards in for loops. See Q243 for coalescing operators (??, ?:, <?, >?) that provide another approach to handling unset values.

Example

defines module qa.flow.guard.switchstatement

  defines function

    getPriority()
      <- rtn <- Integer()
      rtn: 2

    getUnsetPriority()
      <- rtn <- Integer()

  defines program

    GuardSwitchDemo()
      stdout <- Stdout()

      // === BASIC SWITCH GUARD ===

      switch priority <- getPriority() then priority
        case 1
          stdout.println("Low priority")
        case 2
          stdout.println("Medium priority")
        case 3
          stdout.println("High priority")
        default
          stdout.println("Unknown priority")

      // === SWITCH EXPRESSION WITH GUARD ===

      label <- switch priority <- getPriority() then priority
        <- rtn as String: "Unknown"
        case 1
          rtn: "Low"
        case 2
          rtn: "Medium"
        case 3
          rtn: "High"
        default
          rtn: "Other"
      stdout.println("Priority label: " + label)

      // === GUARD WITH UNSET VALUE (entire switch skipped) ===

      switch priority <- getUnsetPriority() then priority
        case 1
          stdout.println("This should not print")
        default
          stdout.println("Default should not print either")

      stdout.println("After guarded switch with unset value")

Common mistakes

E01072 — EK9 has no return or break statements. Guard variables in switch combine declaration and isSet checking. Each case is self-contained with no fallthrough. See ek9 -h E01072 for details.

Incorrect:

priority <- getPriority()
      if not priority?
        return
      switch priority
        case 1
          stdout.println("Low priority")
          break

Correct:

switch priority <- getPriority() then priority
        case 1
          stdout.println("Low priority")
        case 2
          stdout.println("Medium priority")

E01072 — EK9 has no return statement. Switch expressions use a declared return variable. The value is returned implicitly through the variable. See ek9 -h E01072 for details.

Incorrect:

switch priority <- getPriority() then priority
        case 1
          return "Low"

Correct:

label <- switch priority <- getPriority() then priority
        <- rtn as String: "Unknown"
        case 1
          rtn: "Low"
Other ways to ask this
  • Can I guard a switch with a variable declaration?
  • How does switch with guard work in EK9?
  • What happens if a switch guard is unset?

Coming from another language?

Java: Java 21+ pattern matching in switch cannot combine guard declarations. Must write: 'var v = getValue(); switch (v) { ... }' as two steps. Variable leaks into outer scope. Kotlin: 'when' blocks cannot combine guard declaration with control expression. Must nest: 'val v = expr; when (v) { ... }'. Swift: 'switch expr' cannot combine optional binding with switch control. Must nest: 'if let v = expr { switch v { ... } }'. Go: No guard declarations in switch. Must declare and check separately. EK9: 'switch value <- getResult() then value' combines declaration, isSet check, and switch control in one line. Guards scope the variable to the switch block.

Keywords: switch, control, isset, null-safe, branch, declaration, expression, scope, given, safe, case, guard, flow, unset, condition