A terminal operation ends a sequence or flow pipeline by producing a result or by starting work whose purpose is its effects. Transformations such as map and filter are intermediate: they return another sequence or flow and defer execution. A terminal operation is the point where the deferred chain is evaluated. section 6.2.1 section 17.1
Force a sequence pipeline to run
```kotlin val result = listOf(1, 2, 3, 4) .asSequence() .map { it * it } .filter { it % 2 == 0 } .toList() // [4, 16] ``` `toList()` is terminal. Without it, the chain only describes a `Sequence`; the `map` and `filter` lambdas have not run. Calling `toList()` requests the result and performs the postponed computations. section 6.2.1
Sequence evaluation proceeds element by element rather than completing each transformation across the whole input. Each element is mapped and then filtered before the next element is considered. This can avoid work when a terminal operation needs only an early result. section 6.2.1
Stop after the needed sequence value
```kotlin val firstSquareOver3 = listOf(1, 2, 3, 4) .asSequence() .map { it * it } .find { it > 3 } // 4 ``` `find` is terminal: it triggers the pipeline and returns the first matching element. Here, evaluation can stop after the square of `2` is found, leaving later elements unprocessed. section 6.2.1
Terminal operations can finish a pipeline in several ways:
- Materialize transformed values into a collection with `toList()`. section 6.2.1
- Compute an aggregate such as `sum()`; a generated sequence of natural numbers remains unevaluated until `sum()` is called. section 6.2.2
- Return one matching value with `find`, or test for a condition with `any` while traversing generated parent directories. section 6.2.1 section 6.2.2
Laziness is useful, not automatic optimization. Sequences can avoid intermediate collections and skip unnecessary elements, especially for large inputs or pipelines that stop early. But each intermediate sequence stores a lambda, and terminal evaluation calls through the chain; ordinary collection operations may be a good fit for smaller collections. section 10.2.3
For Kotlin `Flow`, terminal operators play the same triggering role, but they execute a stream of values over time. Intermediate flow operators return cold downstream flows; a terminal operator executes the upstream flow. `collect` commonly performs an action for every emitted value, while `first` or `firstOrNull` obtains one value and can cancel upstream work afterward. section 17.2 section 17.4
Collect or take one flow value
```kotlin runBlocking { getTemperatures() .onEach { log(it) } .collect() val reading = getTemperatures().first() } ``` `collect()` runs the flow through completion unless its coroutine scope is cancelled. `first()` instead returns an individual emitted value, allowing the upstream flow to be cancelled after that value arrives. Because terminal flow operators execute the flow, they are suspending functions. section 17.4
Compare an intermediate chain, which returns another sequence, with the terminal step that requests its actual result. section 6.2.1
Figure 17.1 You distinguish between intermediate and terminal flow operators. Where intermediate operators return a modified flow, terminal operators return an actual result. They do so by executing the code in the flow.
Use the flow diagram to distinguish a modified flow returned by an intermediate operator from a terminal operator that executes it. section 17.1


