Function composition is a mathematical concept that allows us to combine two or more functions into a new function where the result of each function is passed to the next one.
A key to function compositions is having functions that are composable, meaning they should have 1 input argument and 1 output value.
We can write a compose function like this:
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x)
After turning all functions provided to an array-like object via the spread operator we can use the reduceRight method on the functions provided. The first parameter of the callback is the current argument (v). The second argument is the current function (f). The first value is the X. Then we call each function with the current argument and the result is used for the next call.
We also have something called a pipe function. It's basically the same thing:
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
Notice that the implementation is the same as the compose function except we are using reduce instead of reduceRight, which reduces left to right instead of right to left. I prefer using the pipe function because I think its more intuitive, but this is down to personal preferences.
A big advantage of functional compositions is splitting up larger functions into smaller ones, making them easier to reuse.
In this example, we have an array of numbers:
const numbers = [1, 2, 3, 4, 5, 6, 7]
In this case, we want to first double all the numbers in the array, then remove all numbers below 10, and finally calculate the sum of the numbers left. We can do this using function chaining like this.
const sumOfArray = numbers
.map(num => num * 2)
.filter(num => num > 10)
.reduce((a, c) => a + c)
console.log(sumOfArray) // Output: 26
A better way would be to break it out to smaller functions:
Double all numbers in an array:
const dubbleArr = arr => arr.map(num => num * 2)
Filter out all numbers below 10 in an array:
const over10Array = arr => arr.filter(num => num > 10)
Calculate the sum of the numbers in an array:
const sumOfArray = arr => arr.reduce((a, c) => a + c)
In our pipe function, we provide a list of functions we want to use and then we specify the array we want to use it on.
const sum = pipe(
dubbleArray,
over10Array,
sumOfArray
)(numbers)
console.log(sum) // Output: 26
This was a small example of how functional compositions work. For more information about functional compositions you can read this articel.
If you want to learn more about functional programming in JavaScript I can recommend this course and this book.
Top comments (1)
Good summary Martin.
Though method-chaining looks quite similar to a functional pipeline, it has a number of drawback.
I could go on. :)
Another side-by-side example of the two approaches....