DEV Community

Cover image for A short introduction to Functional Composition
Martin Persson
Martin Persson

Posted on

A short introduction to Functional Composition

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Filter out all numbers below 10 in an array:

const over10Array = arr => arr.filter(num => num > 10)
Enter fullscreen mode Exit fullscreen mode

Calculate the sum of the numbers in an array:

const sumOfArray = arr => arr.reduce((a, c) => a + c)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
functional_js profile image
Functional Javascript • Edited

Good summary Martin.

Though method-chaining looks quite similar to a functional pipeline, it has a number of drawback.

  • For one, it's Object-oriented programming (a can of worms).
  • Another is if we use one built-in method, then we can't use any custom methods, and we certainly don't want to add custom methods to another's object.
  • FP allows us to separate the data from the operations. OOP commingles those, resulting in spaghettification as system size increases.
  • FP give us more opportunity to use Point-free programming (Tacit programming)

I could go on. :)

Another side-by-side example of the two approaches....

Method Chaining vs Functional Pipelining