How do I use a guard in the expression form of a while loop?
← Control Flow · Ref: Q1354
The while loop has an expression form (it yields a value via a returning variable) and that form accepts the same guards as every other control-flow construct. The guard is written in the header, BEFORE the loop control, separated by 'then' (or 'with').
WHILE EXPRESSION THAT RE-POINTS AND CHECKS
A ':=' pre-flow assigns the right-hand side to an existing variable (an unset value included) and checks nothing, so the check goes in the control: 'then reading? and looping'. If reading is unset, the loop body never runs and the expression yields the returning variable's initial value:
reading as Integer? looping <- true total <- while reading := nextReading() then reading? and looping <- rtn as Integer: 0 rtn: rtn + reading looping: false // total is 0 if nextReading() was unset, otherwise the accumulated value
WHILE EXPRESSION WITH A :=? GUARD
Assign-if-unset (:=?) only assigns when the guard variable is currently unset; it still gates the body on the resulting set-state:
cached as Integer? loopOnce <- true lazy <- while cached :=? nextReading() then loopOnce <- rtn as Integer: -1 rtn: cached loopOnce: false
HEADER ORDER MATTERS
The guard comes FIRST, then the control condition after 'then' (or 'with'):
while <guard> then <control>
Not 'while <control> then <guard>'. The pre-flow runs once at entry. A '<-' or ':=?' guard that leaves its variable unset skips the body; after a ':=' the control's own 'x?' does that.
RETURNING VARIABLE
The returning variable ('<- rtn as T: initial') is declared as the first line of the body and MUST have an initial value, so a skipped body still yields a defined result. This is why a guard-unset while expression returns the initialiser.
See Q77 for while guards in statement form. See Q81 for the while expression form without a guard. See Q1361 for the same guard used uniformly across all six expression-form constructs. See Q74 for the guard operators.
Example
defines module qa.controlflow.guardwhileexpression defines function nextReading() <- rtn as Integer: 42 defines program GuardWhileExpressionDemo() stdout <- Stdout() // while EXPRESSION that re-points 'reading' and checks it in the control reading as Integer? looping <- true total <- while reading := nextReading() then reading? and looping <- rtn as Integer: 0 rtn: rtn + reading looping: false stdout.println(`accumulated total is ${total}`) // while EXPRESSION with a :=? assign-if-unset guard cached as Integer? loopOnce <- true lazy <- while cached :=? nextReading() then loopOnce <- rtn as Integer: -1 rtn: cached loopOnce: false stdout.println(`lazy value is ${lazy}`)
Common mistakes
E08050 — A body-skipping guard ('<-', ':=?') or a failed 'x?' control can skip the while body entirely, and on that path the result is the returning variable's INITIAL value. Declared uninitialised ('<- rtn as Integer?') there would be nothing to return, so the compiler rejects it. Give the return an initial value. See ek9 -h E08050 for details.
Incorrect:
<- rtn as Integer? rtn: reading
Correct:
<- rtn as Integer: 0 rtn: rtn + reading
Other ways to ask this
- Can a while loop that returns a value also use a guard variable?
- How do the :=? guard and a re-pointing ':=' work in a while expression?
- while loop as an expression with a guard operator
- How do I combine an assign-if-unset guard with a while loop that yields a result?
Coming from another language?
Java: no equivalent. A while loop is a statement, never an expression, and cannot declare a loop-scoped guarded variable. You would write a nullable variable, a manual isSet check, and a mutable accumulator across three or more statements. Kotlin: while is a statement; you would use a sequence/fold to get a value. Rust: 'while let Some(x) = iter.next()' binds and loops but is still a statement, not a value-producing expression. EK9: the while expression yields a value AND accepts a guard (<-, :=?) or an explicit 'x := f() then x?' in one header, with the returning variable guaranteeing a defined result even when the guard is unset.
Keywords: expression, isset, value, loop, assignment, then, while, control, flow, returning, :=?, guard