Exploring Code #15: Function Composition
Here's another quick post for you - this time about function composition.
Basically, this concept is the result of combining two functions in one go. So the returned result of a first function is then immediately pumped into the next.
const add = x => x + 2
const square = x => x * x
const squareAdd = square(add(10));
console.log(squareAdd);
// 144
You can think of the above as a function that takes the returned value of a previous function and then does something to that value, but working from right to left.
Eric Elliot describes it like this:
Put simply, a composition of functions f and g can be defined as f(g(x)), which evaluates from the inside out
Really it's just a nested function at the end of the day. So why not just nest a function or even just use reduce?
Well that's exactly what you'd do with a 'compose' function - which is one of the most common use cases for function composition.
Using a Compose Function
The syntax for a compose function looks like this: taken from freeCodeCamp
Recommended by LinkedIn
compose = (fn1, fn2) => value => fn2(fn1(value))
The weirdest part about function composition to me is getting used to the double arrows - that's actually currying at work.
So what's an actual practical use case for this bad boy?
The one that I have used recently is reducing down a string into a dollar amount, but you can also compose an array of objects to grab products by specific parameters.
Let's say we have a cart object and want to get all of the products that currently have a discount and add a little badge to their name within the cart drawer.
Here's a pure JS way of getting started.
const compose = (...fns) => (val) => fns.reduceRight((acc, fn) => fn(acc), val);
async function getCart() {
const result = await fetch("/cart.json");
if (result.status === 200) {
return result.json();
}
throw new Error(`Failed to get request, Shopify returned ${result.status} ${result.statusText}`);
}
const cart = await getCart();
const cartItems = cart.items;
const discountedProducts = (products) => cartItems.filter((product) => product.discounts.amount);
const productNames = (products) => cartItems.map((product) => product.handle);
const discounts = compose(productNames, discountedProducts);
discounts(cartItems);
This will get you started. It will currently spit out any product that has a discount attached to it on the product level with the cart.json.
You'd then feed that product in another function and inject the badges that call out the discounted items in your cart.
Obviously, with ajax carts, that's all done asynchronously, so you'd have to hook it up to a mutation observer or something similar to have it fire on every fetch call to the cart.
But still a nice little example of using function composition.