Loop while a connection value remains set, processing each iteration.

← Control Flow · Ref: Q1150

Fetch the first value before the loop, test it in the while condition, and fetch the next one at the end of the body:

  current <- source.getConnection()
  while current?
    stdout.println(current)
    current: source.getConnection()

The loop ends cleanly when getConnection() returns an unset value.

A while GUARD does not do this. In 'while conn <- supplier.getConnection() with counter < maxIters' the guard runs ONCE, before the loop: it takes one connection (and skips the loop entirely if that is unset), and only the 'with' condition is re-checked on each iteration. So conn stays the first connection for every pass. Use a guard to set up the loop, not to poll. See Q77 for while guard details. See Q971 for declaration guards.

Example

defines module qa.controlflow.whileguard

  defines class

    ConnectionSupplier
      remaining <- 0

      ConnectionSupplier()
        -> count as Integer
        this.remaining: count

      getConnection()
        <- rtn as String: String()
        if remaining > 0
          rtn: `Connection-${remaining}`
          remaining: remaining - 1

      override operator ? as pure
        <- rtn as Boolean: remaining?

  defines program

    WhileGuardDemo()
      stdout <- Stdout()

      source <- ConnectionSupplier(4)
      processed <- 0

      // Loop while getConnection() returns a SET value: fetch first, re-fetch at the end of the body
      current <- source.getConnection()
      while current?
        stdout.println(current)
        processed: processed + 1
        current: source.getConnection()

      stdout.println(`Processed ${processed} connections`)

      // A while GUARD runs ONCE, before the loop: conn is the first connection on every pass,
      // and only the 'with' condition is re-checked
      supplier <- ConnectionSupplier(2)
      counter <- 0
      maxIters <- 3
      while conn <- supplier.getConnection() with counter < maxIters
        stdout.println(`Guarded: ${conn}`)
        counter: counter + 1

Common mistakes

E07390 — Using a constant Boolean literal (true) as the while-guard's 'with' condition is a pointless expression; supply a real terminating condition instead. See ek9 -h E07390 for details.

Incorrect:

while conn <- supplier.getConnection() with true

Correct:

while conn <- supplier.getConnection() with counter < maxIters

E01073 — 'null' does not exist in EK9; use tri-state semantics (unset/set) with the '?' operator and guards instead. See ek9 -h E01073 for details.

Incorrect:

counter <> null

Correct:

counter < maxIters
Other ways to ask this
  • Write code to use a while guard that loops while a value is set
  • I have a supplier that eventually returns unset and need to loop until that happens
  • Given a function that returns set values for N calls then unset, process each value
  • In Java I'd use while ((val = getNext()) != null). Write the EK9 guard equivalent
  • Does a while guard call its function again on every iteration?

Coming from another language?

Java: while ((val = getNext()) != null) { process(val); }. Go: for val := getNext(); val != nil; val = getNext(). Rust: while let Some(v) = get_next(). EK9: val <- getNext(), then while val? with val: getNext() at the end of the body. A while guard (while val <- getNext() with cond) runs once, before the loop, so it does not poll.

Keywords: poll, while, loop, guard, isset, unset, declaration, connection