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 WITH A ?= GUARD

The guarded assignment (?=) assigns the right-hand side to the guard variable and checks whether it is SET. If unset, the whole loop body is skipped and the expression yields the returning variable's initial value:

  reading as Integer?
  looping <- true
  total <- while reading ?= nextReading() then 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 guard is evaluated once at entry; if it leaves the guard variable unset the body never runs.

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 three guard operators.

Example

defines module qa.controlflow.guardwhileexpression

  defines function

    nextReading()
      <- rtn as Integer: 42

  defines program

    GuardWhileExpressionDemo()
      stdout <- Stdout()

      // while EXPRESSION with a ?= guarded-assignment guard
      reading as Integer?
      looping <- true
      total <- while reading ?= nextReading() then 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

E02001 — The guard is the pre-flow part and must come FIRST in the header; the loop control comes after 'then' (or 'with'). Writing the control first and the guard second is not valid. See ek9 -h E02001.

Incorrect:

total <- while looping then reading ?= nextReading()
        <- rtn as Integer: 0
        rtn: rtn + reading

Correct:

total <- while reading ?= nextReading() then looping
        <- rtn as Integer: 0
        rtn: rtn + reading
        looping: false
Other ways to ask this
  • Can a while loop that returns a value also use a guard variable?
  • How do ?= and :=? guards work in a while expression?
  • while loop as an expression with a guard operator
  • How do I combine a guarded assignment 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 (<-, ?=, :=?) in one header, with the returning variable guaranteeing a defined result even when the guard is unset.

Keywords: flow, control, :=?, assignment, while, isset, loop, guard, value, expression, ?=, guarded, then, returning