Compose and pipe are two sides of the same functional programming coin. The base implementations are short — but interviewers push further with async variants, error propagation, and the ability to explain why one direction is idiomatic over the other.
What are compose() and pipe()?
compose(f, g, h) returns a new function that applies h first, then g, then f — right-to-left. This mirrors mathematical function composition: f(g(h(x))).
pipe(f, g, h) is the same idea but left-to-right: apply f first, then g, then h. This reads like a Unix pipeline or a sequence of transformations — more natural for most developers.
Both are at their core a reduce over an array of functions — each function receives the output of the previous one and passes its result to the next.
Real-world use cases:
- Data transformation pipelines — parse → validate → normalize → format as separate composable steps
- Redux middleware —
applyMiddlewareuses compose to chain store enhancers - Selector composition — deriving computed values from state by composing selectors
- Functional utility libraries — Ramda, Lodash/FP, and RxJS pipelines are built on this pattern
The interview escalates in two directions. First, the multi-argument first function: the initial function in the chain can take multiple arguments; only its return value is passed forward. Second, async compose/pipe: when any function in the chain returns a Promise, the whole pipeline must become async — you replace reduce with an async loop using await.
The Problem
"Implement compose(...fns) that takes any number of functions and returns a new function. When called, it applies the functions right-to-left, passing the result of each as input to the next."
The interviewer extends:
"Now implement pipe(...fns) — same idea but left-to-right. Then make pipeAsync that handles functions returning Promises."
Thought Process
Both functions share the same structure: a reducer over an array of functions.
composeusesreduceRight— start from the last function, work leftwardpipeusesreduce— start from the first function, work rightward
For the multi-argument case: the first function in the execution order (rightmost in compose, leftmost in pipe) is called with all the initial arguments via rest params. Every subsequent function receives exactly one argument — the result of the previous call.
For async: replace the synchronous reduce loop with an async loop. Each step awaits the result before passing it to the next function. This handles both sync and async functions in the same pipeline.
Step 1 — compose(): Right-to-Left
Loading editor...
Step 2 — Cleaner compose() with reduceRight
The standard idiom avoids the index check by initializing the accumulator with the first call:
Loading editor...
Step 3 — pipe(): Left-to-Right
pipe is compose with the array processed in forward order:
Loading editor...
Step 4 — pipeAsync(): Async Pipeline
When any function returns a Promise, chain with await:
Loading editor...
Full Solution
Loading editor...
What Interviewers Are Testing
reducevsreduceRight— understanding why compose usesreduceRight(or reversing +reduce)- Multi-arg first function — knowing only the entry point handles spread args; all subsequent functions take one
- Async awareness — replacing
reducewith afor...of+awaitloop (notreducewith async callbacks, which breaks) - Symmetry of compose/pipe — explaining that they're identical except for the direction of traversal
- Edge cases — empty
fns, singlefns(identity / passthrough)
Interview Tips
- Start with
pipe— it reads left-to-right like English and is easier to explain. Then show compose as "the same thing, reversed." - Don't use
reduceforpipeAsync—reducewith async callbacks doesn't actually await each step; you need afor...ofloop orreducewithpromise.then()chaining. Mention this explicitly — it's a common trap. - Relate to something real — "Redux's
applyMiddlewareuses compose to chain enhancers" shows you've seen this in production code. - Name the FP concept — "This is function composition — the output of one function becomes the input of the next. It's how you build pipelines of pure transformations."