Suggest an editImprove this articleRefine the answer for “Implementing compose”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`compose()` is a combinator that assembles several functions into one: `compose(f, g, h)(x)` is equivalent to `f(g(h(x)))`, so it runs them right to left.** The shortest implementation rests on `reduceRight`: the accumulator travels through the list of functions from the last one to the first. If every function is unary a one-liner is enough; if the last function must accept several arguments, you wrap the accumulator in an array and unpack it with spread. ```javascript const composeUnary = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x); const trim = s => s.trim(); const toInt = s => parseInt(s, 10); const inc = n => n + 1; composeUnary(inc, toInt, trim)(' 41 '); // 42 ``` **Key point:** `compose` is built on `reduceRight` and runs right to left; `pipe` is built on `reduce` and runs left to right.Shown above the full answer for quick recall.Answer (EN)Image**`compose()` is a combinator that assembles several functions into one: `compose(f, g, h)(x)` is equivalent to `f(g(h(x)))` and runs them right to left.** The whole implementation boils down to folding a list of functions: `reduceRight` for `compose` and `reduce` for its mirror image `pipe`. ## Theory ### TL;DR - `compose(f, g, h)(x)` equals `f(g(h(x)))`, executing right to left. - The core implementation is `fns.reduceRight((v, fn) => fn(v), x)`. - To let the last function in the chain take several arguments, keep the accumulator in an array and unpack it with spread. - The async variant wraps the first result in `Promise.resolve` and stitches the rest together with `.then`. - In TypeScript you write overloads for each arity, because a general variadic type is hard to infer. - In production it is handy to have both: `compose` (right to left) and `pipe` (left to right), plus their async versions. ### Quick example ```javascript const compose = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x); const trim = s => s.trim(); const toInt = s => parseInt(s, 10); const inc = n => n + 1; const parseAndInc = compose(inc, toInt, trim); console.log(parseAndInc(' 41 ')); // 42 ``` The string is trimmed first, then parsed into a number, and only then incremented: the functions read right to left. ### Minimal implementation (ES6) A generic version in which the last handler can take many arguments: ```javascript // Generic version: the last handler can take many arguments const compose = (...fns) => (...args) => fns.reduceRight( (acc, fn) => [fn(...acc)], args )[0]; // Example const trim = s => s.trim(); const toInt = s => parseInt(s, 10); const inc = n => n + 1; const parseAndInc = compose(inc, toInt, trim); parseAndInc(' 41 '); // 42 ``` The trick is that the accumulator is always an **array of arguments**. The seed value is `args`, and every step returns `[fn(...acc)]`, that is an array of one element again. At the end you just take `[0]`. ### A variant for unary functions If every function takes exactly one argument, the array wrapper is redundant and the implementation becomes shorter and slightly faster: ```javascript const composeUnary = (...fns) => x => fns.reduceRight((v, fn) => fn(v), x); ``` This is the most common form: in functional code chains are almost always made of unary transformations. ### Promise-aware compose The async version works with both sync and async functions: ```javascript const composeAsync = (...fns) => (...args) => fns .slice(0, -1) .reduceRight( (p, fn) => p.then(res => fn(res)), Promise.resolve(fns[fns.length - 1](...args)) ); // Example const fetchUser = async id => ({ id, name: 'Tim' }); const getName = u => u.name; const shout = s => s.toUpperCase(); const getUserNameLoud = composeAsync(shout, getName, fetchUser); getUserNameLoud(7).then(console.log); // 'TIM' ``` The last function in the list (`fetchUser`) is called separately and its result is immediately normalised with `Promise.resolve`. The remaining functions are attached with `.then`, so each one receives an already unwrapped value, whether it is sync or async. ### A type-safe version for TypeScript A general type for a variadic function is hard to infer, so in practice people write overloads. The ones below cover up to 4 functions and can be extended the same way. ```typescript type Unary<A, R> = (a: A) => R; export function compose<A, R>(f1: Unary<A, R>): Unary<A, R>; export function compose<A, B, R>( f1: Unary<B, R>, f2: Unary<A, B> ): Unary<A, R>; export function compose<A, B, C, R>( f1: Unary<C, R>, f2: Unary<B, C>, f3: Unary<A, B> ): Unary<A, R>; export function compose<A, B, C, D, R>( f1: Unary<D, R>, f2: Unary<C, D>, f3: Unary<B, C>, f4: Unary<A, B> ): Unary<A, R>; export function compose(...fns: Function[]) { return (x: unknown) => fns.reduceRight((v, f) => f(v), x); } ``` The overloads stitch the types of neighbouring functions together: the output of `f2` must match the input of `f1`, otherwise the compiler flags it right away. ### pipe and useful notes You often need `pipe()` as well, which runs functions left to right: ```javascript const pipe = (...fns) => (...args) => fns.reduce((acc, fn) => [fn(...acc)], args)[0]; const parseAndInc2 = pipe(trim, toInt, inc); parseAndInc2(' 41 '); // 42 ``` - If you compose **object methods**, do not forget `this`: bind them with `fn.bind(obj)`. - Try to keep functions **pure** and **unary**: composition then stays simpler and more predictable. - In production it is convenient to have both: `compose` (right to left) and `pipe` (left to right), plus `composeAsync` / `pipeAsync`. ### Common mistakes - **Reaching for `reduce` instead of `reduceRight`.** That gives you `pipe`, not `compose`, and the chain runs in reverse. - **Forgetting the seed value in `reduce`.** Without it the first accumulator is a function from the list rather than the input data. - **Expecting a plain `compose` to unwrap a Promise.** The sync version passes the Promise itself along; you need `composeAsync`. - **Losing `this` on methods.** `compose(obj.method)` calls the method without its context; use `obj.method.bind(obj)`. - **Counting on several arguments in the middle of the chain.** Only the last (rightmost) function takes multiple arguments; every later step receives exactly one value. - **Composing impure functions.** Side effects in the middle of a chain make the result depend on call order and timing.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.