The following code is written in a functional manner. There is a user object which is going to make a purchase. The process is as follow: an item is added to the cart, tax is added, the item is bought and pushed into the purchase history array of the user. Then finally the original cart is emptied. This is all done through the use of the reduce function that has a compose function passed into it.
What I'm struggling to understand is how the reduce function works in this case. From my understanding here the compose function is passed in as the callback function for reduce. within the compose function f represents the accumulator and g represents an item in the array.
const user = {
name: 'Kim',
active: true,
cart: [],
purchases: []
}
const compose = (f,g) => (...args) => {
console.log(1, f);
console.log(2, g);
return f(g(...args));
}
// The below code is commented out so that I can visualize it in a more simple manner
//purchaseItem (
// emptyCart,
// buyItems,
//applyTaxToItems,
// addItemToCart
//)(user, {name: 'laptop', price: 244});
function purchaseItem(...fns) {
console.log(fns);
return fns.reduce(compose);
}
// Here is each piece broken down to be passed into reduce and the arguments the compose function takes
console.log([emptyCart , buyItems, applyTaxToItems, addItemToCart].reduce(compose)(user, {name: 'laptop', price: 244}));
function addItemToCart(user, item) {
const updateCart = user.cart.concat(item);
return Object.assign({}, user, { cart: updateCart });
}
function applyTaxToItems(user, item) {
return user
}
function buyItems(user, item) {
return user
}
function emptyCart(user, item) {
return user
}
The output is as follows:
1 [Function]
2 [Function: addItemToCart]
1 [Function]
2 [Function: applyTaxToItems]
1 [Function: emptyCart]
2 [Function: buyItems]
{ name: 'Kim',
active: true,
cart: [ { name: 'laptop', price: 244 } ],
purchases: [] }
I tried to map the flow of the f and g element. I understand that f will hold whatever value the compose function returns but why is the initial value an anonymous function. Furthermore, why does the item element start with the last element in the array and work backwards? I'm also confused as to why emptyCart function becomes the f value in the last cycle of the reduce. If anyone could explain this to me I would be extremely grateful. Thank you.
Your logs aren't mapping the flow of execution correctly.
You're logging f
first and g
second, but for f(g(x))
, g
is evaluated first and f
second. f(g(x)
can be read "f after g of x" or "f of g of x".
In the same way, when you reduce an array of functions with a reducer like f(g(...x))
, they're going to be evaluated in reverse order, since your resulting function behaves like f(g(h(x)))
.
See the code below for more of an explanation. compose2
is the same as your compose
function, but with more heavy duty logging.
If you run the code below, you might get a better idea of what's going on. To evaluate the function created by the reduction/composition, we evaluate multiple functions of the form f(g(...args))
.
Notice how the results of g(...args)
all propagate up before the final result propagates back down.
const compose2 = (f, g, i) => {
const name = `${f.name} after ${g.name}`;
const h = {[name]: (...args) => {
console.log(i, 'f:', f.name);
console.log(i, 'g:', g.name);
console.log(i, 'args to g:', ...args);
console.log(i, 'g(...args):', g(...args));
console.log(' ');
const res = f(g(...args));
console.log(i, 'result:', res);
return res;
}}[name];
return h;
}
const f_xe = x => x + 'e',
f_xd = x => x + 'd',
f_xc = x => x + 'c',
f_xy = (x, y) => x + y;
console.log([f_xe, f_xd, f_xc, f_xy].reduce(compose2)('a','b'));