How do I process items until a condition is met without break?

← Control Flow Without break/continue/return · Ref: Q287

In Java you write: while(true) { item = next(); if (done(item)) break; process(item); }. EK9 replaces this with guard-driven termination: the while loop condition itself controls when to stop.

THE PROBLEM

Processing items until a termination condition is the most common use of while(true) with break. The break is buried inside the loop body, making the termination condition hard to find.

PATTERN 1: WHILE WITH GUARD VARIABLE

Use a Boolean flag that controls the while loop:

  done <- Boolean(false)
  idx <- 0
  while ~done and idx < length items
    item <- items.getOrDefault(idx, "")
    if item == "STOP"
      done: true
    else
      process(item)
    idx: idx + 1

The termination condition is in the while expression, not buried in the body. Test the flag's value (~done), not whether it is set (~done?): Boolean(false) is already set, so ~done? is false and the loop would never run.

PATTERN 2: COUNTING WITH FOR-RANGE

When processing up to a known maximum:

  for count in 1 ... maxItems
    if ~finished
      item <- getItem(count)
      if isTerminal(item)
        finished: true
      else
        process(item)

PATTERN 3: STREAM WITH HEAD

When you want the first N items that satisfy a condition:

  results <- cat items | filter by isActive | head 10 | collect as List of String

The pipeline stops after 10 active items. No loop, no break, no counter.

GUARD-DRIVEN VS BREAK-DRIVEN

Break-driven: the loop runs forever, break is the only way out. The termination logic is scattered through the body.
Guard-driven: the while condition states exactly when the loop ends. The body focuses on processing.

See Q66 for while loops. See Q144 for no break. See Q125 for head.

Example

#!ek9
defines module qa.without.processuntil

  defines function

    isActive() as pure
      -> item as String
      <- active as Boolean: item <> "STOP" and length item > 0

    processWithLimit()
      ->
        dataItems as List of String
        readLimit as Integer
      <- itemsRead as Integer: 0

      for pos in 0 ... length dataItems - 1
        if itemsRead < readLimit
          itemsRead: itemsRead + 1

  defines program

    ProcessUntilDemo()
      stdout <- Stdout()

      // === PATTERN 1: WHILE WITH GUARD VARIABLE ===

      items <- ["alpha", "bravo", "charlie", "STOP", "delta", "echo"]
      done <- Boolean(false)
      idx <- 0
      processed <- 0
      while ~done and idx < length items
        item <- items.getOrDefault(idx, "")
        if item == "STOP"
          done: true
        else
          stdout.println(`Processing: ${item}`)
          processed: processed + 1
        idx: idx + 1
      stdout.println(`Processed ${processed} items before STOP`)

      // === PATTERN 2: COUNTING WITH FOR-RANGE ===

      numbers <- [10, 20, 30, 40, 50, 60, 70]
      runningTotal <- 0
      totalLimit <- 100
      exceeded <- Boolean(false)
      for count in 0 ... length numbers - 1
        if ~exceeded
          currentNum <- numbers.getOrDefault(count, 0)
          if runningTotal + currentNum > totalLimit
            exceeded: true
          else
            runningTotal: runningTotal + currentNum
      stdout.println(`Running total (stopped before exceeding 100): ${runningTotal}`)

      // === PATTERN 3: STREAM WITH HEAD ===

      allItems <- ["", "alpha", "bravo", "", "charlie", "STOP", "delta", "echo", "foxtrot"]
      activeItems <- cat allItems | filter by isActive | head 3 | collect as List of String
      stdout.println(`First 3 active items: ${activeItems}`)

      // === BOUNDED PROCESSING WITH INDEX ===

      dataItems <- ["read1", "read2", "read3", "read4", "read5"]
      itemsRead <- processWithLimit(dataItems, 3)
      stdout.println(`Bounded read processed ${itemsRead} items`)

Common mistakes

E01070 — EK9 has no break statement. To stop processing when a condition is met, set a guard flag variable and check it in the while condition. See ek9 -h E01070 for details.

Incorrect:

break

Correct:

done: true
Other ways to ask this
  • How do I replace while(true) with break-on-condition in EK9?
  • How do I consume items until done without break in EK9?
  • How do I write a processing loop that stops on a condition in EK9?

Coming from another language?

Java: while(true) { if (done) break; } or do-while with condition. Python: while True: if done: break. Rust: loop { if done { break; } } or while !done. Go: for { if done { break } }. Kotlin: while(true) { if (done) break }. JavaScript: while(true) { if (done) break; }. EK9: while with guard or flag variable, for-range for bounded processing, stream head for take-first-N.

Keywords: alternative, condition, consume, done, exhaust, guard, isset, loop, migrate, no-break, no-return, null-safe, process, safe, stop, terminate, until, while