How do guard variables work in for loops?
← Control Flow · Ref: Q76
In EK9, guard variables in for loops combine variable creation with an isSet check before the loop begins. The guard executes ONCE as a preamble, not on each iteration.
BASIC FOR-IN GUARD
Use the declaration operator (<-) with 'with' to link the guard to the loop:
for config <- getConfig() with item in items process(config, item)
If getConfig() is unset, the entire loop is skipped. No exception.
FOR-RANGE GUARD
Guards also work with range-based for loops:
for config <- getConfig() with i in 1 ... 10 stdout.println(config.host + ": iteration " + $i)
GUARD TIMING
The guard evaluates ONCE before iteration begins. This is different from while loop guards that re-evaluate on each iteration. The guard sets up a precondition for the entire loop.
FOR EXPRESSION WITH GUARD
For loops can be expressions that return a value:
result <- for config <- getConfig() with i in 1 ... 3 <- rtn as String: "no config" rtn: config.host + " " + $i
If the guard is unset, the expression evaluates to the default return value.
WITHOUT A GUARD
The standard for-in loop needs no guard when iterating a collection:
for item in items process(item)
Guards add safety when a precondition must be met before iterating.
See Q64 for for-range loops. See Q65 for for-in loops. See Q74 for guards in if statements. See Q77 for guards in while loops (with re-evaluation).
Example
defines module qa.flow.guard.forloop defines record ServerConfig host <- String() port <- Integer() default operator ? defines function getServerConfig() <- rtn <- ServerConfig() rtn.host: "api.example.com" rtn.port: 443 getNoConfig() <- rtn <- ServerConfig() defines program GuardForDemo() stdout <- Stdout() items <- ["alpha", "beta", "gamma"] // === FOR-IN WITH GUARD === for config <- getServerConfig() with item in items stdout.println(`${config.host} processing: ${item}`) // === FOR-RANGE WITH GUARD === for config <- getServerConfig() with i in 1 ... 3 stdout.println(`Iteration ${i} on ${config.host}`) // === GUARD WITH UNSET VALUE (loop skipped) === for config <- getNoConfig() with item in items stdout.println("This should not print: " + item) stdout.println("After guarded for with unset value")
Common mistakes
E01072 — EK9 has no return statement. Guard variables in for loops combine the precondition check and iteration in one statement, replacing the check-then-return pattern. See ek9 -h E01072 for details.
Incorrect:
config <- getServerConfig() if not config? return for item in items stdout.println(`${config.host} processing: ${item}`)
Correct:
for config <- getServerConfig() with item in items stdout.println(`${config.host} processing: ${item}`)
E01070 — EK9 has no break statement. For loops with guards always iterate through the full range. Use stream pipelines with head if you need to stop early. See ek9 -h E01070 for details.
Incorrect:
for config <- getServerConfig() with i in 1 ... 3 stdout.println(`Iteration ${$i} on ${config.host}`) if i == 2 break
Correct:
for config <- getServerConfig() with i in 1 ... 3 stdout.println(`Iteration ${i} on ${config.host}`)
Other ways to ask this
- Can I guard a for loop with a variable declaration?
- How does the for loop guard work in EK9?
- What happens if a for loop guard is unset?
Coming from another language?
Java: No guard concept in for loops. Must check preconditions separately: 'var c = getConfig(); if (c != null) { for (item : items) { ... } }'. Python: No guard in for loops. Must wrap: 'config = get_config(); if config: for item in items: ...'. Rust: No guard in for loops. Must wrap: 'if let Some(c) = get_config() { for item in items { ... } }'. Go: No guard in for loops. Must check nil separately. EK9: 'for config <- getConfig() with item in items' combines precondition check and iteration in one statement. Guard evaluates once before loop begins.
Keywords: condition, control, isset, null-safe, preamble, branch, declaration, precondition, iteration, for, safe, loop, guard, flow, range