How do I exit a nested loop without break or labels?

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

EK9 has no break, no labels, and no goto. When you need to exit nested loops, the solution is function decomposition: extract the inner loop into its own function.

THE PROBLEM

In Java you write: outer: for (row : grid) { for (item : row) { if (found(item)) break outer; } }. In Python you use flag variables or exceptions. These patterns are error-prone and obscure intent.

PATTERN 1: DECOMPOSE INTO FUNCTIONS

Extract the inner loop into a separate function that returns unset if nothing found:

  searchRow()
    -> row as List of String, target as String
    <- found as String: String()
    for item in row
      if item == target
        found: item

Then guard on the result in the outer loop:

  for row in grid
    if match <- searchRow(row, target)
      result: match

The outer loop naturally stops assigning once result is set, and the inner function handles one level of nesting.

PATTERN 2: FLATTEN AND STREAM

For simple search across nested data, flatten with concatenation and use filter + head:

  allItems <- cat row1 + row2 + row3 | filter by isTarget | head 1 | collect as List of String

This avoids nested loops entirely by working with a flat pipeline.

PATTERN 3: WHILE WITH GUARD

Use a flag to control the outer loop:

  located <- Boolean()
  rowIndex <- 0
  while rowIndex < length grid and ~located?
    row <- grid.getOrDefault(rowIndex, List() of String)
    for item in row
      if item == target
        located: true
    rowIndex: rowIndex + 1

The while loop exits when located becomes set.

KEY PRINCIPLE

If you need nested break, your function is doing too much. Decompose it. Each function handles one level of iteration.

See Q144 for why break was removed. See Q145 for basic break replacement. See Q146 for function decomposition.

Example

defines module qa.without.nestedloop

  defines function

    searchRow()
      ->
        row as List of String
        target as String
      <- found as String: String()
      for item in row
        if item == target
          found: item

    isMatch() as pure
      -> item as String
      <- matched as Boolean: item == "cherry"

  defines program

    NestedLoopDemo()
      stdout <- Stdout()

      // === PATTERN 1: DECOMPOSE INTO FUNCTIONS ===

      row1 <- ["apple", "banana", "cherry"]
      row2 <- ["date", "elderberry", "fig"]
      row3 <- ["grape", "honeydew", "kiwi"]
      grid <- [row1, row2, row3]

      target <- "cherry"
      result <- String()
      for row in grid
        if match <- searchRow(row, target)
          result :=? match
      if result?
        stdout.println(`Found by decomposition: ${result}`)
      else
        stdout.println("Not found by decomposition")

      // === PATTERN 2: FLATTEN AND STREAM ===

      allItems <- row1 + row2 + row3
      streamResult <- cat allItems | filter by isMatch | head 1 | collect as List of String
      stdout.println(`Found by stream: ${streamResult}`)

      // === PATTERN 3: WHILE WITH GUARD ===

      located <- Boolean()
      rowIndex <- 0
      while rowIndex < length grid and ~located?
        row <- grid.getOrDefault(rowIndex, List() of String)
        for item in row
          if item == target
            located: true
        rowIndex: rowIndex + 1
      stdout.println(`Located by while: ${located?}`)

      // === SEARCH FOR MISSING ITEM ===

      missing <- String()
      for row in grid
        if match <- searchRow(row, "mango")
          missing :=? match
      if ~missing?
        stdout.println("Mango not found as expected")

Common mistakes

E01070 — EK9 has no break statement. To exit a nested loop, decompose into functions and use guard assignment (:=?) so the first successful result wins. See ek9 -h E01070 for details.

Incorrect:

break

Correct:

result :=? match
Other ways to ask this
  • How do I stop both inner and outer loops without break in EK9?
  • What replaces labelled break for nested loops in EK9?
  • How do I search a grid or matrix without break in EK9?

Coming from another language?

Java: break with label (break outer;) for nested loops. Python: flag variables or raise/except hack. Rust: break with loop labels ('outer: loop { break 'outer; }). Go: break with label, or goto. C/C++: goto or flag variables. Kotlin: break with label (@outer). JavaScript: break with label. EK9: decompose inner loop into a function, guard on result, or flatten and stream.

Keywords: label, no-return, inner, nested, break, loop, grid, decompose, alternative, matrix, migrate, exit, outer, search, no-break, function, flatten