Can I use a guard (including '?=') inside a switch or loop expression in EK9?
← Control Flow · Ref: Q837
Yes. A body-skipping guard ('<-' declaration, '?=' guarded assignment, ':=?' assign-if-unset) works in an expression-form switch/for/while/do-while/try, just as in statement form. The guard gates the whole construct: if the guarded value is unset the body is skipped, and the left-hand side takes the returning variable's INITIAL value - 'the initialiser is the guard's else'.
BECAUSE OF THAT, THE RETURN MUST BE INITIALISED
Declare the return with an initial value ('<- rtn as T: default'). If you declare it uninitialised ('<- rtn as T?') and set it only inside the (skippable) body, the compiler rejects it with E08050 RETURN_NOT_ALWAYS_INITIALISED, because the skip path would leave the LHS unset.
THIS EXAMPLE
A switch expression with a '?=' guard and an initialised return. The guard sets 'temperature', the switch runs, and 'resultText' gets a case value; had the guard left 'temperature' unset, 'resultText' would be the initial "Unknown".
NOT THE STREAM FORM
This holds for switch/for/while/do-while/try, which all declare a returning param. A STREAM expression does not: it ends in '| collect as T', so there is nowhere to write the initial value that would be the result on the skip path. A guarded stream expression is therefore rejected with E08056:
collected <- for limit <- getLimit() then i in 1 ... 3 | collect as List of Integer //E08056
The stream STATEMENT form is fine with a guard - there is no result to leave undeclared. See Q1381.
See Q759 for guard patterns. See Q69 for multi-case switch. See Q1381 for guards on stream pipelines.
Example
defines module qa.controlflow.guard.expression defines function currentTemperature() as pure -> country as String <- temperature as Integer? if country == "GB" temperature :=? 18 else if country == "DE" temperature :=? 35 else temperature :=? 25 defines program GuardDemo() stdout <- Stdout() //Valid: a '?=' guard in a switch expression. The guard gates the switch; if 'temperature' were left //unset the body would be skipped and 'resultText' would take the return's initial value ("Unknown"). temperature as Integer? resultText <- switch temperature ?= currentTemperature("GB") with temperature <- result as String: "Unknown" case < 12 result: "Cold" case < 25 result: "Moderate" default result: "Warm" stdout.println(resultText)
Common mistakes
E08050 — A body-skipping guard ('<-', '?=', ':=?') can skip the switch body entirely, and on that path the result is the returning variable's INITIAL value. Declared uninitialised ('<- result as String?') there would be nothing to return on the skip path, so the initialiser is what makes a guarded switch EXPRESSION legal. The stream form of this trap is E08056 - see QA1381, which has an actual pipeline. See ek9 -h E08050 for details.
Incorrect:
<- result as String?
Correct:
<- result as String: "Unknown"
E08050 — In an expression form, a guard can skip the body, so the return must be initialised at declaration. Give it a default ('<- result as String: "Unknown"'); an uninitialised '<- result as String?' set only in the body is E08050 RETURN_NOT_ALWAYS_INITIALISED.
Incorrect:
<- result as String?
Correct:
<- result as String: "Unknown"
Other ways to ask this
- Do guards work in switch/for/while/try expressions?
- What does 'result <- switch temp ?= getValue() with temp' do?
- Why does a guard in an expression need an initialised return (E08050)?
- Can I guard a stream pipeline expression?
Coming from another language?
Kotlin 'when' and Rust 'match' are expressions but have no guard-gating pre-flow. EK9 unifies it: the same guard syntax works in statement AND expression forms, and the mandatory return initialiser is the value the LHS receives when the guard skips.
Keywords: initialised return, guard, conditional, switch, E08050, guarded assignment, expression