I recently tried to understand function composition in javascript. I understood what currying is and how it works, but I saw this code and I can't really understand it. It's function composition using .reduce(...)
. This is the code:
const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));
const add5 = x => x + 5;
const add10 = x => x + 10;
const multiply = (x, y) => x * y;
const multiplyAndAdd5 = compose(
add10,
add5,
multiply
);
console.log(multiplyAndAdd5(5, 2));
What I don't understand is the reduce function, I tried to break it down into this :
const compose = (...fns) => fns.reduce(
(f, g) => (...args) => {
console.log("f: ");
console.log(f);
console.log("g: ");
console.log(g);
for(let i = 0 ; i < 3 ; i++){
console.log('');
}
return f(g(...args))
},
);
In the console this shows up:
f:
(...args) => {
console.log("f: ");
console.log(f);
console.log("g: ");
console.log(g);
// console.log("args: ");
// console.log(...args);
// console.log(f(g(...args…
g:
(x, y) => x * y
f:
x => x + 10
g:
x => x + 5
15
25
What I don't understand is what the accumulator actually is in that function and in the first part of the reduce function, f is the function itself so, what is f(g(...args)) in that situation ?
Does anyone know how function composition work in javascript using .reduce() ?
reduce
can be thought of as putting an operator between each pair of items in a sequence. E.g. reducing [1, 2, 3]
using *
would produce 1*2*3
(by performing ((1)*2)*3)
; in the same way, reducing with function composition would reduce [f, g, h]
into f∘g∘h
(by performing ((f∘g)∘h)
, also written as (...args) => f(g(h(...args)))
.
When you don't give an initial value to reduce
, it takes the first element of the array; so accumulator starts off as f
. Operating on the element g
, it returns a new function (...args) => f(g(...args))
(also known as f∘g
). In your example, the initial accumulator is add10
, the element is add5
, and the result is a function (...args) => add10(add5(...args))
; let's call it add10AfterAdd5
.
In the next iteration, the accumulator is f∘g
, and the element is h
. The result is a new function (...args) => (f∘g)(h(...args))
, which is equivalent to (...args) => f(g(h(...args)))
(also known as f∘g∘h
). In your example, the accumulator is add10AfterAdd5
, and the element is multiply
; the result is (...args) => add10AfterAdd5(multiply(...args))
, or (...args) => add10(add5(multiply(...args)))
, or add10AfterAdd5AfterMultiply
.