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);
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);
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);
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);
We often see the functions on a separate line whether you are using a pipe or compose.
const composeResult2 = compose(
dreamBig,
dreamSmall,
dreamLittle
)(8);
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);
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
Here is what we can do
const pipeResult4 = pipe(
dreamBig,
dreamSmall,
dreamLittle,
multiplyBy2
)(8);
console.log(pipeResult3);
console.log(pipeResult4);
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"
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;
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
);
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));
So the major differences between compose and pipe is the order of their execution.
Top comments (1)
Is there ever a situation to use
compose
overpipe
since they're the same but compose has a less intuitive execution order?