When should I use a stream pipeline vs a loop in EK9?
← Streams and Pipelines · Ref: Q237
EK9 provides both stream pipelines and loops. Use streams for filtering, transforming, limiting, and chaining operations. Use loops for side effects and condition-based iteration.
USE STREAMS WHEN
Filtering (filter by), transforming (map with), limiting (head N as early exit), deduplication (sort | uniq), chaining multiple operations, or grouping (group by).
USE FOR-IN LOOPS WHEN
Side effects on every item (printing, saving). Simple accumulation where a loop is more readable.
USE FOR-IN EXPRESSIONS WHEN
Complex multi-variable accumulation or fold/reduce logic. See Q82.
USE WHILE LOOPS WHEN
Condition-based iteration (polling, retrying, external APIs). See Q66.
DECISION SUMMARY
Filter or limit? Stream (filter by, head) Transform each item? Stream (map with) Early exit? Stream (head N) — See Q125 Side effects on all items? For-in loop Complex accumulation? For-in expression or collect as Condition-based? While loop
See Q51 for abstract functions in pipelines. See Q52 for dynamic functions. See Q54 for Predicate/Comparator types. See Q64-Q66 for loop types. See Q80/Q82 for loop expressions. See Q89 for stream basics. See Q122 for collect as. See Q125 for head as early exit. See Q145 for replacing break/continue. See Q235 for stream operations reference. See Q261 for idiomatic patterns.
Example
defines module qa.streams.vsloops defines function isEven() as pure -> num as Integer <- rtn as Boolean: num mod 2 == 0 doubleIt() as pure -> num as Integer <- rtn as Integer: num * 2 intToString() as pure -> num as Integer <- rtn as String: $num defines program StreamsVsLoopsDemo() stdout <- Stdout() numbers <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // === STREAM: Filter + Transform + Limit === topThreeDoubledEvens <- cat numbers | filter by isEven | map with doubleIt | head 3 | collect as List of Integer stdout.println(`Stream (filter+map+head): ${topThreeDoubledEvens}`) // === STREAM: Sum with collect as Integer === total <- cat numbers | collect as Integer stdout.println(`Stream sum: ${total}`) // === FOR-IN LOOP: Side effects === stdout.println("Loop (side effects):") for num in numbers stdout.println(` Processing ${num}`) // === FOR-IN EXPRESSION: Accumulation === sum <- for n in numbers <- rtn <- 0 rtn: rtn + n stdout.println(`Loop expression sum: ${sum}`) // === FOR-IN EXPRESSION: Complex accumulation === evenCount <- for n in numbers <- rtn <- 0 if n mod 2 == 0 rtn: rtn + 1 stdout.println(`Even count via loop: ${evenCount}`) // === STREAM: Same result as above === evenList <- cat numbers | filter by isEven | collect as List of Integer stdout.println(`Even count via stream: ${length evenList}`)
Common mistakes
E07520 — A predicate function used with 'filter by' must return Boolean. Returning Integer (e.g., the modulo result) instead of Boolean triggers E07520. See ek9 -h E07520 for details.
Incorrect:
<- rtn as Integer: num mod 2
Correct:
<- rtn as Boolean: num mod 2 == 0
Other ways to ask this
- What is the difference between a stream and a loop in EK9?
- Should I use cat pipe collect or a for loop in EK9?
- When are streams better than loops in EK9?
- How do I choose between a stream pipeline and a for-in loop in EK9?
Coming from another language?
Java/Kotlin: Streams vs for-each, similar trade-offs but EK9 head replaces break. Python: list comprehensions vs for-loops. Go: only for-loops. EK9: cat|filter|map|collect for chains, for-in for side effects, for-in expression for accumulation, head replaces break.
Keywords: pipe, collect, while, vs, head, when, stream, transform, for, versus, choose, accumulate, loop, filter, effect, comparison, side, guide, decide