Do guard variables work the same way in every expression-form control-flow construct?
← Control Flow · Ref: Q1361
Yes. EK9 offers a UNIFORM guard mechanism across all six expression-form control-flow constructs: while, do/while, for-range, for-in, switch/given, and try. In every one, a guard in the header (<- declaration or :=? assign-if-unset) is evaluated once at entry; if it leaves the guard variable unset the whole body is skipped and the expression yields the returning variable's initial value. The same operators, the same skip-on-unset semantics, the same 'returning variable must be initialised' rule — everywhere.
THE SAME :=? GUARD, SIX CONSTRUCTS (g starts uninitialised; each yields source() when set):
while: wResult <- while g :=? source() then loop ... do/while: dResult <- do g :=? source() ... while loop for-range: rResult <- for g :=? source() with i in 1 ... 1 ... for-in: fResult <- for g :=? source() with item in [2] ... switch: sResult <- switch g :=? source() with g ... try: tResult <- try g :=? source() ...
HEADER POSITION
The guard is always the pre-flow, written FIRST: after 'while'/'do'/'for'/'switch'/'try', before the construct's own control ('then'/'with' condition, range, collection, or trailing 'while'). It is never placed after the control.
IF IS THE EXCEPTION
'if' has no expression form — its value-producing counterpart is the ternary ('cond <- a : b'). Every OTHER construct has both statement and expression forms, and the guard behaves identically in both.
WHY UNIFORM
One rule to learn, no per-construct special cases, and the returning-variable-initialised requirement means a guard-skipped body can never yield an undefined value. This is enforced by the compiler (E08050).
See Q1354 (while), Q1355 (do-while), Q1356 (for-range), Q1357 (for-in), Q1359 (switch), Q1358 (try) for each construct in detail. See Q74 for the guard operators. See Q1360 for the ternary (the if-expression equivalent).
Example
defines module qa.controlflow.guardsuniform defines function source() <- rtn as Integer: 7 defines program GuardsUniformDemo() stdout <- Stdout() // The SAME :=? guard, in each of the six expression-form constructs. // Every guard variable starts uninitialised, so ':=?' assigns source(); it is set, so every body runs // once and the result is 7. // The SAME :=? guard, with the SAME skip-on-unset semantics and the SAME // 'returning variable must be initialised' rule, shown in three representative // expression forms. source() is set, so each body runs once and yields 7. // (do-while, for-in and switch behave identically — see Q1355/Q1357/Q1359.) // while expression: guard, then 'then <control>' loopFlag <- true whileGuard as Integer? whileValue <- while whileGuard :=? source() then loopFlag <- rtn as Integer: -1 rtn: whileGuard loopFlag: false // for-range expression: guard, then 'with <var> in <range>' rangeGuard as Integer? rangeValue <- for rangeGuard :=? source() with i in 1 ... 1 <- rtn as Integer: -2 require i? rtn: rangeGuard // try expression: guard right after 'try' tryGuard as Integer? tryValue <- try tryGuard :=? source() <- rtn as Integer: -3 rtn: tryGuard stdout.println(`while=${whileValue} forRange=${rangeValue} try=${tryValue}`)
Common mistakes
E08050 — In every expression form the returning variable must be INITIALISED so a guard-skipped body still yields a defined value. Declaring it uninitialised ('<- rtn as Integer?') is rejected by E08050. See ek9 -h E08050.
Incorrect:
whileValue <- while whileGuard :=? source() then loopFlag <- rtn as Integer?
Correct:
whileValue <- while whileGuard :=? source() then loopFlag <- rtn as Integer: -1
Other ways to ask this
- Is the guard syntax uniform across while, for, switch and try expressions?
- Can I use the same :=? guard in a while, for, switch and try that all return values?
- guards across all control flow expression forms
- one guard pattern for every value-producing construct
Coming from another language?
No mainstream language offers a single guard mechanism that is uniform across all loop, switch and try constructs AND lets each yield a value. Rust has 'while let'/'if let' but not for switch/try and they are statements; Swift has 'if let'/'guard let' but not across loops as value-producers. EK9 unifies declaration and assign-if-unset guards over while, do-while, for-range, for-in, switch and try, each with a value-producing expression form and a compiler-guaranteed initialised result.
Keywords: :=?, consistent, try, flow, while, switch, returning, guard, do-while, expression, uniform, for, control