How do I exit a loop early or skip iterations in EK9?
← Control Flow · Ref: Q145
EK9 has no break or continue. Stream pipelines with head, filter, and skip are the designed replacements.
REPLACING BREAK WITH HEAD
Where other languages use a loop with break to find the first match, EK9 uses head 1:
first <- cat items | filter by isLong | head 1 | collect as List of String
The head operation stops the pipeline after N items. No elements beyond that point are processed.
REPLACING CONTINUE WITH FILTER
Where other languages skip unwanted items with continue, EK9 uses filter:
valid <- cat items | filter by isNonEmpty | collect as List of String
The filter operation passes only items that match the predicate. Items that do not match are discarded.
REPLACING COUNTED BREAK WITH HEAD N
Where other languages count items and break at a limit, EK9 uses head N:
topFive <- cat items | sort | head 5 | collect as List of String
This takes exactly 5 items and stops. No counter variable, no break condition.
REPLACING SKIP-AHEAD WITH SKIP
Where other languages use continue with a counter to skip initial items, EK9 uses skip:
afterHeader <- cat lines | skip 3 | collect as List of String
This discards the first 3 elements and passes the rest through.
WHEN LOOPS ARE STILL APPROPRIATE
For-in and for-range loops are still the right tool for side effects that process every item:
for item in items stdout.println(`Processing: ${item}`)
Loops work when you need to process ALL items and perform actions. Streams work when you need to select, transform, or limit.
COMBINING OPERATIONS
Pipeline operations compose naturally:
results <- cat items | filter by isValid | skip 2 | head 3 | collect as List of String
This filters, skips 2 valid items, then takes the next 3. Each operation has a single responsibility.
See Q64 for for-range loops. See Q65 for for-in loops. See Q89 for stream pipeline basics. See Q125 for head, tail, and skip details. See Q131 for Python comprehension migration. See Q133 for tee and uniq operations. See Q144 for why break and continue were removed. See Q282 for nested loop patterns. See Q288 for before and after migration examples.
Example
defines module qa.flow.philosophy.replacingbreak defines function isLong() as pure -> item as String <- long <- false maxItems <- 5 long: length item > maxItems isNonEmpty() as pure -> item as String <- nonEmpty as Boolean: length item > 0 isValid() as pure -> item as String <- valid <- false divisor <- 2 valid: length item > divisor defines program ReplacingBreakContinueDemo() stdout <- Stdout() items <- ["", "hi", "apple", "banana", "", "cherry", "date", "elderberry", "fig"] // === REPLACING BREAK: FIND FIRST LONG ITEM === firstLong <- cat items | filter by isLong | head 1 | collect as List of String stdout.println(`First long item: ${firstLong}`) // === REPLACING CONTINUE: SKIP EMPTY STRINGS === nonEmpty <- cat items | filter by isNonEmpty | collect as List of String stdout.println(`Non-empty: ${nonEmpty}`) // === REPLACING COUNTED BREAK: TOP 3 === topThree <- cat items | filter by isNonEmpty | sort | head 3 | collect as List of String stdout.println(`Top 3 sorted: ${topThree}`) // === REPLACING SKIP-AHEAD === afterSkip <- cat items | filter by isValid | skip 2 | collect as List of String stdout.println(`After skipping 2 valid: ${afterSkip}`) // === COMBINING: FILTER + SKIP + HEAD === window <- cat items | filter by isValid | skip 1 | head 3 | collect as List of String stdout.println(`Window (skip 1, head 3): ${window}`) // === LOOP FOR SIDE EFFECTS === stdout.println("Processing all valid items:") for item in items if length item > 0 stdout.println(` -> ${item}`)
Common mistakes
E01070 — EK9 has no break statement. Use stream pipelines with head 1 to find the first matching item. The pipeline stops processing after the first match. See ek9 -h E01070 for details.
Incorrect:
firstLong <- String() for item in items if isLong(item) firstLong: item break
Correct:
firstLong <- cat items | filter by isLong | head 1 | collect as List of String
E01071 — EK9 has no continue statement. Use stream pipelines with filter to keep only items matching a predicate, replacing the skip-with-continue pattern. See ek9 -h E01071 for details.
Incorrect:
nonEmpty <- List() of String for item in items if length item == 0 continue nonEmpty += item
Correct:
nonEmpty <- cat items | filter by isNonEmpty | collect as List of String
E01072 — EK9 has no return statement. Declare the return variable with '<-' and assign it conditionally. The compiler ensures all paths initialise the return variable. See ek9 -h E01072 for details.
Incorrect:
<- long <- false return true
Correct:
<- long <- false
Other ways to ask this
- What replaces break and continue in EK9?
- How do I find the first match without break in EK9?
- How do I skip items in a loop without continue in EK9?
- How do I replace break and continue with stream operations in EK9?
Coming from another language?
Java: break to exit loop, continue to skip iteration, Streams with limit() and skip() as alternative. Python: break to exit loop, continue to skip, list comprehensions with conditions as alternative. Rust: break to exit loop, continue to skip, iterators with take() and skip() as alternative. Go: break to exit loop, continue to skip, no built-in stream alternative. C/C++: break and continue in loops. Kotlin: break and continue, sequences with take() and drop(). JavaScript: break and continue, array methods filter() and slice(). EK9: no break or continue, stream pipelines with head (replaces break), filter (replaces continue), skip (replaces counted skip), and tail (last N).
Keywords: stream, control, loop, early, break, match, condition, filter, skip, flow, pipeline, first, head, continue, iteration, alternative, replace, migrate, branch, exit