Terminals
the terminals
Stages are inert; the terminal is where everything happens. When you write
.sum or .find(p) or .run, that call (an inline macro:
code that runs inside the compiler, guaranteed-inlined at the call site, rewriting the typed
syntax tree) receives the whole chain as a syntax tree and compiles it into one loop. Which terminals
a pipeline has is decided per source shape; Integrating
covers that machinery. This page is what the terminals do.
The families
The familiar collections surface is all here, each in one fused pass:
buildrun · toList · toVector · toSeq · toSet · toArray · toMap · to(Factory) · mkString
aggregatefoldLeft · fold · sum · product · count · count(p) · size · length · agg · aggTo
searchfind · exists · forall · contains · isEmpty · nonEmpty · head · headOption · indexWhere · indexOf · collectFirst
reducereduce · reduceLeft · min · max · minBy · maxBy · …Option variants · last · lastOption
group / rankgroupReduceBy · groupReduce · groupCount · groupSum · groupBy · groupMapReduce · topN · bottomN · topNBy · bottomNBy
multi-outputpartition · span · unzip · sliding · plan
Four of them have their lowerings shown here, because they do things the standard library can't.
Search terminals stop the machine. find plants a done-flag that every loop level
carries, across sources and flatMap nesting alike, so the traversal ends as soon as the answer
exists:
you write
the macro emits
agg answers several questions in one pass. Needing sum, count, and max together is normally
three traversals or a boxing tuple-fold. Here each aggregate rides its
own unboxed accumulator and the loop body is just N statements:
you write
the macro emits
aggTo lands the answers in your own type. Same machine, but the last line of the lowering is
Summary.apply(total, n, top) instead of a tuple you unpack:
you write
the macro emits
groupSum folds groups without boxing. groupBy builds a Map[K, List[A]], pure overhead when
you only wanted an aggregate per key. groupReduceBy (and its groupSum/groupCount spellings)
folds each group as elements stream past into a primitive-keyed open-addressing table: Int keys
and Int/Long/Double accumulators stay unboxed in the hot loop, and boxing happens exactly once
per key, at the final O(#keys) materialization:
you write
the macro emits
The ranking family exists because "top ten by score" should not cost a sort: topNBy(n)(key) keeps
a bounded size-n heap inside the pass (O(N log n) time, O(n) memory, best-first):
you write
the macro emits