Function composition builds a function by connecting smaller functions: one function’s result becomes the next function’s input. It lets each step have a narrow job while the assembled function expresses a larger process. For `f(g(value))`, `g` runs first, so `compose(f, g)` reads from right to left. section 4.3
Count words in a string
Define `split = str => str.split(/\s+/)` and `count = arr => arr.length`. Then `const countWords = compose2(count, split)` creates a reusable function: `countWords('functional programming')` returns `2`. A two-function combinator can be written as `const compose2 = (f, g) => (...args) => f(g(...args))`. section 4.3
Start by identifying steps, then give each one a function. Counting words in a file separates reading, decoding, splitting, and counting. The resulting chain, `compose(count, split, decode, read)`, shows the process without intermediate assignments. Reading the file still has a side effect; keep that step apart from the pure transformations rather than treating the whole chain as pure. section 4.3.1
A chain works best when its steps fit together:
- Prefer predictable, side-effect-free transformations for the core logic; they are easier to rearrange and reason about. section 4.1 section 4.3.1
- Match each stage’s input to the preceding stage’s output. If a function also needs configuration, curry or partially apply it first; `decode('utf8')` leaves a function ready to receive a buffer. section 4.4.1 section 4.4.2
- Choose the presentation that makes the flow clearest: `compose` lists stages right to left, while `pipe` lists them left to right. Curried array operations can also turn a method chain into a composed, point-free function. section 4.8 section 4.7
Composition is not limited to strings. A balance calculation can filter blocks, collect transactions, map them to amounts, reduce them, and round the result. A hashing operation can compose property selection, assembly, and a configured hash function. These examples show how a larger operation can be assembled from replaceable parts. section 4.7 section 4.4.2
A direct function chain assumes usable inputs and well-defined outputs. Validation and exception handling complicate that flow; algebraic data types offer a way to compose operations while representing failure. The same output-to-input idea also appears in promise chains and observable operators, where values move through asynchronous or streamed stages. section 5.0 section 8.2.2 section 9.4.3










