How do I use a guard in a for-in loop, including the expression form?

← Control Flow · Ref: Q1357

A for-in loop ('for item in collection') accepts a guard in its header, written BEFORE the loop variable and separated by 'with' (or 'then'). The for-in loop also has an expression form yielding a value via a returning variable.

FOR-IN EXPRESSION WITH A ?= GUARD

  prefix as String?
  joined <- for prefix ?= getPrefix() with item in items
    <- rtn as String: ""
    rtn: `${rtn}${prefix}${item}`

The guarded assignment (?=) assigns getPrefix() to 'prefix' and checks it is SET. If unset, the loop body is skipped and 'joined' is the initial "". (Note: build strings with interpolation `${...}`, not 3+ chained '+', which EK9 rejects with E11068.)

FOR-IN STATEMENT WITH A :=? GUARD

  marker as String?
  count <- 0
  for marker :=? getPrefix() with item in items
    if marker?
      count: count + 1

GUARD vs LOOP VARIABLE
The guard variable ('prefix', 'marker') is separate from the loop variable ('item'). Order is 'for <guard> with <var> in <collection>'; the guard cannot go after the collection.

See Q65 for the plain for-in. See Q1356 for for-range guards. See Q1361 for guards across all expression-form constructs. See Q76 for guards in for loops overview.

Example

defines module qa.controlflow.guardforinexpression

  defines program

    GuardForInExpressionDemo()
      stdout <- Stdout()
      items <- ["alpha", "beta", "gamma"]

      // for-in EXPRESSION with a ?= guard (guard FIRST, then 'with item in collection')
      prefix as String?
      joined <- for prefix ?= getPrefix() with item in items
        <- rtn as String: ""
        rtn: `${rtn}${prefix}${item}`
      stdout.println(`joined: ${joined}`)

      // for-in STATEMENT with a :=? guard
      marker as String?
      count <- 0
      for marker :=? getPrefix() with item in items
        if marker?
          count: count + 1
      stdout.println(`count is ${count}`)

  defines function

    getPrefix()
      <- rtn as String: "> "

Common mistakes

E02001 — The guard is the pre-flow and must come BEFORE the loop variable: 'for <guard> with <var> in <collection>'. See ek9 -h E02001.

Incorrect:

joined <- for item in items with prefix ?= getPrefix()
        <- rtn as String: ""
        rtn: `${rtn}${prefix}${item}`

Correct:

joined <- for prefix ?= getPrefix() with item in items
        <- rtn as String: ""
        rtn: `${rtn}${prefix}${item}`
Other ways to ask this
  • Can a for item in collection loop use a guard variable?
  • for-in as an expression with a ?= guard
  • guarded assignment when iterating a collection
  • How do I guard a for-each loop that returns a value?

Coming from another language?

Java: enhanced-for is a statement; it cannot bind a guarded, isSet-checked header variable nor produce a value. Kotlin/Swift: for-each is a statement; value production needs map/fold. EK9: for-in takes a guard (<-, ?=, :=?) before the loop variable, and its expression form yields the returning variable, with the guard able to skip iteration entirely when unset.

Keywords: returning, with, isset, guarded, iterate, :=?, collection, for-each, for-in, for, expression, ?=, guard