How do guard variables work in while loops?
← Control Flow · Ref: Q77
In EK9, a while loop can start with a guard: a declaration (or assignment) followed by 'with' (or 'then') and the loop condition. The guard runs ONCE, before the loop, exactly like a for loop guard. It is an entry gate: if it leaves its variable unset, the loop does not run at all. Once the loop is running, only the condition after 'with' is re-checked on each iteration; the guard is not called again.
BASIC WHILE GUARD
The guard declares the variable once; the condition controls the iterations:
while value <- supplyCounter() with counter > 0 stdout.println(`Counter: ${counter} value: ${value}`) counter: counter - 1
supplyCounter() is called once. The loop then runs while 'counter > 0' holds.
UNSET GUARD SKIPS THE LOOP
If the guard's value is unset, the condition is never checked and the body never runs. No exception is thrown. This is a safe alternative to checking for null before a while loop.
CONSUMING AN ITERATOR
Because the guard runs once, it is the place to get an iterator; the condition re-checks it each time:
while iter <- names.iterator() then iter.hasNext() stdout.println(iter.next())
LOOPING UNTIL A VALUE BECOMES UNSET
The guard is not re-evaluated, so a guard such as 'while item <- source.next() with item?' would test the same first item forever. To poll until a source runs dry, fetch before the loop and again at the end of the body:
item <- source.next() while item? process(item) item: source.next()
WHILE EXPRESSION WITH GUARD
While loops can be expressions that return a value; the guard sets up the loop variable once:
total <- while counter <- 0 with counter < limit <- rtn <- 0 rtn: rtn + counter counter: counter + 1
If the guard is unset, the expression evaluates to the initial return value.
THREE GUARD OPERATORS IN WHILE
All of them run once, before the loop:
1. Declaration (<-): creates the variable; the loop runs only if it is set while value <- getNext() with condition 2. Assignment (:=): assigns an existing variable, NO isSet check; the condition alone decides while existing := getNext() with condition 3. Assign-if-unset (:=?): fills an existing variable only while it is unset, and the loop runs only if it is set while existing :=? getNext() with condition
To re-point an existing variable and loop only while the new value is set, check it in the condition: 'while existing := getNext() then existing? and (condition)'.
See Q66 for basic while loops. See Q67 for do-while loops. See Q74 for guards in if. See Q76 for guards in for loops. See Q81 for while expressions.
Example
defines module qa.flow.guard.whileloop defines function supplyCounter() <- rtn <- Integer() rtn: 5 supplyNothing() <- rtn as Integer: Integer() supplyStart() as pure -> initial as Integer <- rtn as Integer: initial defines program GuardWhileDemo() stdout <- Stdout() // === BASIC WHILE GUARD: evaluated ONCE, before the loop === counter <- supplyStart(3) while guardedCount <- supplyCounter() with counter > 0 stdout.println(`Counter: ${counter} value: ${guardedCount}`) counter: counter - 1 // === UNSET GUARD: the loop does not run at all === attempts <- supplyStart(2) while missing <- supplyNothing() with attempts > 0 stdout.println(`Never printed: ${missing}`) attempts: attempts - 1 stdout.println(`Unset guard skipped the loop, attempts still ${attempts}`) // === CONSUMING AN ITERATOR: get it once, re-check hasNext() each time === names <- ["Ada", "Grace", "Alan"] while iter <- names.iterator() then iter.hasNext() stdout.println(`Name: ${iter.next()}`) // === ASSIGNMENT (no isSet check) === loops <- supplyStart(2) existing <- Integer() while existing := supplyCounter() with loops > 0 stdout.println("Assigned value: " + $existing) loops: loops - 1 stdout.println("While guard demo complete")
Common mistakes
E07390 — 'while true' is a constant Boolean condition, which EK9 rejects as pointless (and the break such a loop needs does not exist in EK9, E01070). A guard plus a 'with' condition replaces it: the guard runs once, before the loop, and the condition decides each iteration. See ek9 -h E07390 for details.
Incorrect:
while true guardedCount <- supplyCounter() if not guardedCount? break if counter <= 0 break stdout.println(`Counter: ${$counter} value: ${$guardedCount}`) counter: counter - 1
Correct:
while guardedCount <- supplyCounter() with counter > 0 stdout.println(`Counter: ${counter} value: ${guardedCount}`) counter: counter - 1
Other ways to ask this
- Can I guard a while loop with a variable declaration?
- How does the while loop guard work in EK9?
- Is a while loop guard evaluated on every iteration?
Coming from another language?
Java: No guard in while. Must write: 'T v; while ((v = getNext()) != null && v > 0) { ... }' mixing assignment and null check in condition. Error-prone. Python: No guard in while. Walrus operator helps: 'while (v := get_next()) is not None and v > 0:' but only handles None. Rust: 'while let Some(v) = get_next()' for iterator consumption. Close to EK9 but limited to pattern matching. Go: No guard in while (Go only has for). Must use: 'for v := getNext(); v != nil; v = getNext() { ... }'. EK9: 'while value <- getNext() with value > 0' combines declaration, isSet check, and loop condition. The guard runs once, before the loop (an unset guard skips the loop); only the condition after 'with' is re-checked each iteration.
Keywords: iteration, poll, once, branch, terminate, loop, guard, safe, flow, while, null-safe, isset, declaration, condition, consume, control