Easy

compose

Prompt

Implement a compose function that takes any number of functions and returns a new function. When the returned function is called with an argument, it applies the given functions from right to left, passing the result of each to the next.

If no functions are passed, the returned function should return its input unchanged.

Playground

Hint 1

compose is the mirror of pipe. If you've solved pipe, the only difference is the direction: pipe goes left to right, compose goes right to left.

Hint 2

Array.reduceRight() works exactly like reduce() but iterates from the last element to the first. That's exactly the direction compose needs. Alternatively, you can use a for loop that starts from the end.

Solution

Explanation

If you've already solved the pipe question, compose is nearly identical. The only difference is the direction:

  • pipe(a, b, c)(x) applies functions left to right: first a, then b, then c
  • compose(a, b, c)(x) applies functions right to left: first c, then b, then a

So compose(addOne, double)(5) means: run double first (because it's on the right), then run addOne on the result. double(5) gives 10, addOne(10) gives 11.

The code

function compose(...fns) {
return function (value) {
return fns.reduceRight(
(result, fn) => fn(result),
value
);
};
}

compose collects all the functions into the fns array using ... (rest parameters), then returns a new function. When that new function is called with a value, it uses reduceRight to process the functions from the last one to the first.

If you haven't used reduceRight before, it works exactly like reduce, but starts from the end of the array instead of the beginning. It takes two arguments: a callback and an initial value. Here, the initial value is value (whatever the caller passed in). It hands that to the rightmost function, takes the result, hands it to the next function to the left, and keeps going until every function has been applied.

Walking through an example

compose(addOne, double)(5);

The functions array is [addOne, double]. reduceRight starts from the right:

  1. Start with value = 5
  2. Apply double(5)10
  3. Apply addOne(10)11

Result: 11.

If no functions are passed, reduceRight on an empty array simply returns the initial value unchanged, which is exactly the behavior we want.

Alternative with a for loop

If reduceRight doesn't come to mind, a for loop starting from the end does the same thing:

function compose(...fns) {
return function (value) {
let result = value;
for (let i = fns.length - 1; i >= 0; i--) {
result = fns[i](result);
}
return result;
};
}

Both approaches are correct. The reduceRight version is more concise, but the for loop is easier to read if you're not familiar with reduceRight.

pipe is more commonly asked in interviews than compose, but both test the same concept: chaining functions together. Libraries like Lodash provide _.flow (pipe) and _.flowRight (compose). Redux's compose utility uses this exact pattern to combine middleware.