Declarative programming foregrounds the result or transformation you want and leaves most control mechanics in the background. Rather than narrating every loop, branch, and state update, it composes named operations so the code can be read as a flow of intent. This is especially useful when a problem naturally reads as data entering a transformation and leaving as a result. section 6.1.2 section 6.4
Functional pipelines are a common declarative form. Small functions act as predictable building blocks: each accepts inputs and produces an output. When those functions are pure, they neither depend on nor modify external mutable state, so a call can be replaced by its result without changing behavior. That referential transparency makes individual transformations easier to test, compose, and understand in isolation. section 6.1.2
Express a word transformation as a pipeline
A stream can state the transformation directly: filter words longer than three characters, convert them to uppercase, then collect the results. The pipeline exposes the meaningful stages instead of requiring the reader to trace loop counters, temporary collections, and conditional insertion. section 6.1.2
Figure 6.1 Pure function illustrated as an isolated transformation: inputs enter the function, and a single output is produced. The function depends only on its inputs and does not interact with external state. As a result, the same inputs always produce the same output.
Use the diagram as a test for a pure transformation: inputs enter, one output leaves, and no external state participates. This model supports predictable composition, but it does not describe operations such as database writes or clock reads. section 6.1.2
Choose declarative composition when it clarifies the transformation, but keep its limits visible:
- Prefer pipelines when the work is a sequence of transformations and composition makes the flow easier to read. section 6.4
- Use imperative control when work requires a precise action sequence, substantial state mutation, or direct interaction with files, databases, or external services. section 6.1.2 section 6.4
- Do not force every task into functions: an awkward or impure pipeline can be inefficient or obscure intent. Where an API favors functional composition despite impurity, make that impurity explicit. section 6.4
- Treat performance as contextual: pipelines add setup cost for small or simple work, while larger workloads may benefit from composability, lazy evaluation, or parallel processing. section 6.4
A practical heuristic is to ask whether the code’s main story is “transform these values.” If so, name the transformations and compose them. If its main story is “perform these stateful actions in this order,” make the sequence imperative rather than hiding it behind an artificial pipeline. section 6.1.2 section 6.4













