`compose`, and especially `pipe`, are easily among my favorite functions.

This article’s just to have fun and explore different implementations of these two gems. I recommend you understand what they do before reading this; perhaps check out my deep-dive here.

``````pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
``````

Classic.

Starting with the leftmost function, reduce an array of functions to a single value by calling the next function with the previous one’s output.

``````double = (x) => x * 2;
add1 = (x) => x + 1;

pipe(
double,
)(100); // 201
``````

I discovered this implementation through Eric Elliott, and wrote a deep-dive on it here.

Use `reduceRight` to implement `compose`. Now your functions are called from right, to left.

``````compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);

compose(
double,
)(100);
// 202
``````

You could also reverse `fns` and keep using `reduce` (less performant).

``````compose = (...fns) => (x) => fns.reverse().reduce((v, f) => f(v), x);

compose(
double,
)(100); // 202
``````

`reverse` mutates the array, though, so you might copy it first (even less performant).

``````compose = (...fns) => (x) => [...fns].reverse().reduce((v, f) => f(v), x);

compose(
double,
)(100); // 202
``````

Use `reduceRight` to go back to `pipe`.

``````pipe = (...fns) => (x) => [...fns].reverse().reduceRight((v, f) => f(v), x);

pipe(
double,
)(100); // 201
``````

### But They’re All Unary

All the above snippets, by the way, are unary. Each function may only accept a single argument.

If your pipeline’s first function must be nAry (accepting `n` arguments), try this implementation:

``````multiply = (x, y) => x * y;
pipe = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args)));

pipe(
multiply,
)(10, 10); // 101
// Takes multiple args now
``````

This snippet’s from 30secondsofcode.org. Your first (leftmost) function may accept `n` arguments–all others must be unary.

Again, `reduceRight` gives us `compose`. Now your rightmost function may accept `n` arguments. Let’s move `multiply` to the end of the chain.

``````compose = (...fns) => fns.reduceRight((f, g) => (...args) => g(f(...args)));

compose(
multiply
)(10, 10); // 101
// Takes multiple args now
// Put multiply first
``````

Like before, you could reverse the `fns` array and keep using `reduce`:

``````compose = (...fns) =>
[...fns].reverse().reduce((f, g) => (...args) => g(f(...args)));

compose(
multiply
)(10, 10); // 101
``````

If you want to keep `reduce` without the slight performance hit, just switch `g` and `f`:

``````compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

compose(
multiply
)(10, 10); // 101
``````

And use `reduceRight` to switch back to `pipe`.

``````pipe = (...fns) => fns.reduceRight((f, g) => (...args) => f(g(...args)));

pipe(
multiply,
)(10, 10); // 101
// put multiply first now
``````

### Conclusion

Phew! That’s a lot of ways to pipe and compose!

It just proves that, no matter what, you must loop over an array of functions, calling the next one with the previous one’s result.

Doesn’t matter if you use `reduce`, `reduceRight`, switch the invocation order, or whatever else.

If you want `pipe()`, go left-to-right. Want compose()? Go right-to-left.

Plain and simple. Until next time!