DEV Community

Muhammad Muhktar Musa
Muhammad Muhktar Musa

Posted on • Edited on

compose or pipes

Introduction

Functional programming often use pipe and compose functions. They are higher order functions. A higher order function is any function that takes a function as an argument, returns a function or both. We are going to have a look at the compose and pipe functions, the way they are built and a simple explanation of how they work.

Compose

when compose is called on a function, it gives a new function. for example

const dreamBig = (db) => { return console.log('hello' + db / 8) }
const dreamSmall = (ds) => { return console.log('hello' + ds + 6) };
const dreamLittle = (dl) => { return console.log('hello' + dl * 6); }

const result = dreamBig(dreamSmall(dreamLittle(4)));
console.log(result);
Enter fullscreen mode Exit fullscreen mode

the variable result, is a function composition. The dreamBig executes first, and then the dreamSmall and lastly dreamLittle. They are nested functions and they execute from right to left.
A compose function is a nested function that runs from right to left.
To get the compose order from right to left as we see with nested functions calls in our example above, we need a reduceRight() method.

const compose = (...ftn) => val => ftn.reduceRight((prev, fn) => fn(prev), val);
Enter fullscreen mode Exit fullscreen mode

The reduce function takes a list of values and applies a function to each of those values, accumulating a single result.
The method uses the currying method. The function is called or invoked immediately. It starts at the right and terminates at the left.

const composeResult = compose(dreamBig, dreamSmall, dreamLittle)(8)
console.log(composeResult);
Enter fullscreen mode Exit fullscreen mode

pipes

For those that do not like reading from right to left as done in compose, pipes essentially changes the order of compose from left to right. It works like compose but uses the reduceLeft()
method instead.

const pipe = (...ftn) => val => ftn.reduce((prev, fn) => fn(prev), val);
const pipeResult = pipe(dreamLittle, dreamSmall, dreamBig)(8)
console.log(pipeResult);
Enter fullscreen mode Exit fullscreen mode

We often see the functions on a separate line whether you are using a pipe or compose.

const composeResult2 = compose(
  dreamBig,
  dreamSmall,
  dreamLittle
)(8);

Enter fullscreen mode Exit fullscreen mode

The examples we have looked at use a pointer free style and with unary functions, we don't see the parameter passed between each function, only the parameters passed at the end of the compose or
pipe function when immediately invoking the function.

Deep dive into compose and pipe

Let us take a look at an example where we have possibly more than one parameter or when not working with a unary function

const divideBy = (divisor, num) => num / divisor;
//this function requires two parameters, the divisor and the num and it implicitly returns the result
const pipeResult3 = pipe(
  dreamBig,
  dreamSmall,
  dreamLittle,
  x => divideBy(7, x)
)(8);
console.log(pipeResult3);
Enter fullscreen mode Exit fullscreen mode

Look at how we provide divideBy using the pipe method, all the results gotten from each of the functions is being passed to x.
then we use an anonymous function and we call divideBy, we provide a number and x goes into the function. This is how to reduce a function with multiple parameters in a pipe or compose function.
Could we curry the divideby function to get a unary function if we have already hard coded the number in the compose or method pipe.

const multiplyBy = (multiplier) => (num) => num * multiplier;
const multiplyBy2 = multiplyBy(2); //partially applied unary function
Enter fullscreen mode Exit fullscreen mode

Here is what we can do

const pipeResult4 = pipe(
  dreamBig,
  dreamSmall,
  dreamLittle,
  multiplyBy2
)(8);
console.log(pipeResult3);
console.log(pipeResult4);
Enter fullscreen mode Exit fullscreen mode

it will still work like the other unary function. Let us take a look at some other examples

const bahrain = "bahrain is a ccountry situated in the eastern part of arabia precisely the middle east"
Enter fullscreen mode Exit fullscreen mode

here we are just going to count the words in the paragraph. lets define a couple of functions

const spaceSPlit = (str: string) => str.split(' ');
const number = (arr: string | any[]) => arr.length;
Enter fullscreen mode Exit fullscreen mode

The spaceSplit method is going to look for each space in the paragraph while the function number is going to count how many words we have after splitting the string.

const howMany = pipe(
  spaceSPlit,
  number
);
Enter fullscreen mode Exit fullscreen mode

if we create a pipe function and apply it on the string it will invoke first the spaceSplit function and then the number function. We don't have to call the function immediately but we can go ahead and log the function

console.log(howMany(bahrain));
Enter fullscreen mode Exit fullscreen mode

So the major differences between compose and pipe is the order of their execution.

Top comments (1)

Collapse
 
sharlos profile image
Chris

Is there ever a situation to use compose over pipe since they're the same but compose has a less intuitive execution order?