How do I use a :=? assign-if-unset guard in a switch expression?

← Control Flow · Ref: Q1359

The switch (or 'given') expression accepts a guard in its header. The guard runs once, before matching; if it leaves the guard variable unset the whole switch body is skipped and the expression yields the returning variable's initial value.

SWITCH EXPRESSION WITH A :=? GUARD

Assign-if-unset (:=?) sets the control variable only when it is currently unset, then the switch matches on it:

  tier as String?
  discount <- switch tier :=? lookupTier() with tier
    <- rtn as Integer: 0
    case "gold"
      rtn: 20
    case "silver"
      rtn: 10
    default
      rtn: 5

If lookupTier() is unset, 'tier' stays unset, the body is skipped, and 'discount' is the initial 0.

SWITCH STATEMENT WITH A :=? GUARD

The same guard works without a returning variable:

  level as String?
  switch level :=? lookupTier() with level
    case "gold"
      stdout.println("premium")
    default
      stdout.println("standard")

HEADER SHAPE

'switch <guard> with <control>' — the guard is the pre-flow; the value being matched comes after 'with'. Contrast ?= (guarded assignment, always assigns then checks) with :=? (assigns only when unset).

See Q75 for switch guards. See Q837 for the ?= guard in a switch expression. See Q68 for the switch expression. See Q1361 for guards across all expression-form constructs.

Example

defines module qa.controlflow.guardswitchexpression

  defines function

    lookupTier()
      <- rtn as String: "gold"

  defines program

    GuardSwitchExpressionDemo()
      stdout <- Stdout()

      // switch EXPRESSION with a :=? assign-if-unset guard
      tier as String?
      discount <- switch tier :=? lookupTier() with tier
        <- rtn as Integer: 0
        case "gold"
          rtn: 20
        case "silver"
          rtn: 10
        default
          rtn: 5
      stdout.println(`discount is ${discount}`)

      // switch STATEMENT with a :=? guard
      level as String?
      switch level :=? lookupTier() with level
        case "silver"
          stdout.println("standard tier")
        default
          stdout.println("premium tier")

Common mistakes

E02001 — The guard is the pre-flow and comes first; the matched control follows 'with'. Order is 'switch <guard> with <control>'. See ek9 -h E02001.

Incorrect:

discount <- switch tier with tier :=? lookupTier()
        <- rtn as Integer: 0
        case "gold"
          rtn: 20
        default
          rtn: 5

Correct:

discount <- switch tier :=? lookupTier() with tier
        <- rtn as Integer: 0
        case "gold"
          rtn: 20
        default
          rtn: 5
Other ways to ask this
  • switch expression with a guarded assignment
  • Can a switch that returns a value lazily initialise its control variable?
  • assign-if-unset guard in a given/switch
  • How do I default the switch control with :=? before matching?

Coming from another language?

Java: switch is a statement (or, since 14, an expression) but has no header guard and no isSet/assign-if-unset concept; you would default the variable in a separate statement. EK9: the switch/given expression takes a guard (<-, ?=, :=?) that can lazily initialise the control and skip the whole switch when unset, while still yielding the returning variable.

Keywords: default, unset, isset, expression, assign, guard, given, :=?, returning, case, control, switch