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