Function composition builds a new function from existing ones, feeding each result into the next. Two conventions:
pipe(f, g, h)(x)runsf, theng, thenh:h(g(f(x))). Reads in execution order.compose(f, g, h)(x)runshfirst:f(g(h(x))). Mirrors mathematical notation.
Implementation: the returned function takes the input and folds the list of functions over it. pipe uses reduce, compose uses reduceRight; the accumulator is the value flowing through the chain, and the initial value is the input. Passing the input as the initial value also makes the empty case the identity function, with no special code.
Contract: every function except the first should take one argument and return one value. For anything else, adapt with currying or partial application (add(1) instead of add). Only the input value is forwarded: a second argument to the composed function is dropped.
Variants: if the first function must accept several arguments, take (...args) and call first(...args) before reducing (that fails for an empty list, so decide how to handle it). For async steps, chain with promises, as pipeAsync below.