Nowadays it is extremely common to see the usage of Array methods such as
Array.prototype.reduce() to name a few in manipulating list data structures.
You can see a lot of method chaining such as a chain of
Array.prototype.map() to process the transforming of a data, a chain of
Array.prototype.filter() to include/exclude specific items in the array or a mixture of two (2)
Array.prototype.map() and a
Array.prototype.reduce() at the end. Those sorts of chainings are extremely common and that happens very often.
It’s actually a great result having those sorts of chainings to manipulate data but there’s a hidden cost on that in terms of performance and that’s where the Fusion comes in.
⚡ Fuse ’em all
Fusion is a technique of combining pure functions — taking advantage of composition and removing the intermediary copies of data on each layer.
Let’s see an example comparing the non-fusion and a fusion approach.
I would like to start noting that a mapper function that is passed on to the
Array.prototype.map() should always, at least, handle it process in a pure manner to avoid unexpected results. Avoid using it and accessing outside variables or mutating another variable outside its lexical scope. Make it pure.
With the given example, you would think it’s well written and easily understandable but what’s the big deal? What is the hidden cost of having a chain of
Let’s discover it together by unpacking this chain.
Are you surprised? I am too at first.
Each chain of method whether it is
Array.prototype.reduce() comes with a cost. It will result in iterating the whole list again and again which will affect the performance especially if we are talking about large datasets.
You might think why not join the two (2) mapper functions in one call of
Yes we can join them in a single mapper to avoid chaining of
Array.prototype.map() . That is actually a semi-fusion approach already. A full fusion approach is taking advantage of the composition.
I have a question for you, isn’t it inconvenient to manually pass the result from one function to the next function? What if the order is changed? How can we make it better?
Let’s now see how we can make this thing better by applying the Fusion technique.
pipe() utility accepts an infinite number of functions that it will run from left to right. Meaning that it will execute the functions,
multiplyBy2() from left to right and the result of the current function will be passed to the next one. That is kinda the shortcut instead of manually passing the result from one function to another function as we did on our previous example.
When a function is pure it means we can take advantage of composition. The composition is the act of combining pure functions to create a new single function. In our example, we created a new single function that is a composition of the two (2) pure mapper functions using the
Now that we’ve applied the Fusion technique, our code will now handle even large datasets in a single iteration without duplicate iteration cost and also we’ve taken advantage of the composition in here which is great!
🤖 Bringing It All Together
Fusion is a great simple technique to have on our hands. It can help us when we are dealing with List data structures to take advantage of composition and also get the benefits in performance as well.
If we have a chain of maps together, composition is a much better way of declaratively saying what all of those steps are gonna be. Rather than three (3) .map calls, it would be preferrable from functional perspective to use composition. — Kyle Simpsons
If you are interested in checking it out more, here are some references:
- Fusion Explained with Ramda.js (https://www.youtube.com/watch?v=l7cCwl9lIX0)
Thank you for reading. I hope this will help you on your journey! ❤️