How do I convert break-based loops from Java or Python to EK9?

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

This shows four common break-based loop patterns and their EK9 equivalents side by side.

SCENARIO 1: FIND FIRST X

Java: for (String s : items) { if (s.length() > 5) { result = s; break; } }
Python: for s in items: if len(s) > 5: result = s; break
EK9 equivalent uses filter + head:

  results <- cat items | filter by isLong | head 1 | collect as List of String

The pipeline stops after the first long item. No loop variable, no break.

SCENARIO 2: PROCESS FIRST N MATCHING

Java: int count = 0; for (String s : items) { if (isValid(s)) { process(s); if (++count >= 3) break; } }
EK9 equivalent uses filter + head N:

  topN <- cat items | filter by isValid | head 3 | collect as List of String
  for item in topN
    process(item)

The stream selects, the loop processes. Each part has one responsibility.

SCENARIO 3: ACCUMULATE UNTIL LIMIT

Java: int sum = 0; for (int n : numbers) { sum += n; if (sum > 100) break; }
EK9 equivalent uses a guard flag:

  total <- 0
  exceeded <- Boolean(false)
  for num in numbers
    if ~exceeded
      if total + num > 100
        exceeded: true
      else
        total: total + num

The flag replaces break. The termination condition is checked at loop entry.

SCENARIO 4: SKIP ITEMS THEN TAKE SOME

Java: boolean started = false; int taken = 0; for (String s : items) { if (!started) { if (s.equals("START")) started = true; continue; } process(s); if (++taken >= 2) break; }
EK9 equivalent uses skip + head:

  selected <- cat items | skip 2 | head 3 | collect as List of String

For conditional skip, use filter to remove unwanted prefix items.

THE PATTERN

Every break loop decomposes into: what do I want? Express the want as a stream pipeline or a guard-controlled loop.

See Q145 for replacing break and continue. See Q148 for migration rules. See Q275 for AI break patterns.

Example

defines module qa.without.migratebreak

  defines function

    isLong() as pure
      -> item as String
      <- long as Boolean?
      minLongLength <- 5
      long: length item > minLongLength

    isValid() as pure
      -> item as String
      <- valid as Boolean?
      minValidLength <- 2
      valid: length item > minValidLength

    formatItem() as pure
      -> item as String
      <- formatted as String: `[${item}]`

  defines program

    MigrateBreakDemo()
      stdout <- Stdout()

      items <- ["hi", "apple", "banana", "fig", "cherry", "date", "elderberry"]

      // === SCENARIO 1: FIND FIRST LONG ITEM ===
      // Java: for (s : items) { if (s.length() > 5) { result = s; break; } }

      firstLong <- cat items | filter by isLong | head 1 | collect as List of String
      stdout.println(`First long item: ${firstLong}`)

      // === SCENARIO 2: PROCESS FIRST 3 VALID ===
      // Java: count = 0; for (s : items) { if (valid(s)) { process(s); if (++count >= 3) break; } }

      topThree <- cat items | filter by isValid | head 3 | collect as List of String
      for item in topThree
        stdout.println(`Processing valid: ${formatItem(item)}`)

      // === SCENARIO 3: ACCUMULATE UNTIL LIMIT ===
      // Java: sum = 0; for (n : numbers) { sum += n; if (sum > 100) break; }

      numbers <- [10, 25, 30, 45, 50, 60]
      total <- 0
      totalLimit <- 100
      exceeded <- Boolean(false)
      for num in numbers
        if ~exceeded
          if total + num > totalLimit
            exceeded: true
          else
            total: total + num
      stdout.println(`Accumulated total (under 100): ${total}`)

      // === SCENARIO 4: SKIP ITEMS THEN TAKE SOME ===
      // Java: started = false; taken = 0; for (s : items) { if (!started) { continue; } process(s); if (++taken >= 2) break; }

      selected <- cat items | skip 2 | head 3 | collect as List of String
      stdout.println(`Skipped 2, took 3: ${selected}`)

      // === ALL SCENARIOS COMBINED ===

      fruits <- ["fig", "apple", "banana", "cherry", "date", "elderberry", "grape"]
      longFruits <- cat fruits | filter by isLong | skip 1 | head 2 | collect as List of String
      stdout.println(`Skip 1 long, take 2: ${longFruits}`)

Common mistakes

E01070 — EK9 has no break statement. When migrating accumulate-until-limit loops from Java or Python, replace break with a Boolean flag checked in the loop condition. See ek9 -h E01070 for details.

Incorrect:

break

Correct:

exceeded: true
Other ways to ask this
  • What are before and after examples for converting break loops to EK9?
  • How do I migrate a for loop with break to EK9 idioms?
  • What are the EK9 equivalents of common break loop patterns?

Coming from another language?

Java: for loop with break on condition, counter + break for first-N, accumulator + break on limit. Python: for with break, enumerate + break, itertools.islice as alternative. Rust: for with break, iter().take(n).filter(), iter().scan() for accumulation. Go: for with break, manual counter. Kotlin: for with break, take() and filter() on sequences. JavaScript: for with break, Array.find(), Array.some(). EK9: filter + head for selection, guard variables for accumulation, skip + head for windowing.

Keywords: stream, after, before, no-break, no-return, convert, loop, alternative, imperative, refactor, side, migration, migrate, break