Exploring Composition in Javascript
Open Source Your Knowledge, Become a Contributor
Technology knowledge has to be shared and made accessible for free. Join the movement.
Introduction
Insert another intro about functional programming...
Composition
Composition is about creating small functions and creating bigger and more complete functions with them. Think of a function as a brick, composition is how you would make those bricks work together to build a wall or a house.
You might have encoutered composition in mathematics, written like so: f(g(x)). The function f is composed with the function g of x. Or f after g equals f of g of x. After because we evaluate the functions from right to left, from the inside to the outside:
f <-- g <-- x
The output of the precedent function becomes the input of the next. x is the input of g. The output of g(x) becomes the f input.
Examples?
Ok, let's code something then. Imagine that you are a company that is in charge of manipulating text. You receive a bunch of words, and your customers want them back in a certain way.
A client comes at you with a text and says:
I want the words shorter than 5 characters to be uppercased.
We create three functions to execute those actions. One function takes the text and return words in lowercase. The second function looks for short words and upper-case them. Finally, the third recreates the text from the array received.
function words( text ){
return String( text )
.toLowerCase()
.split(/\s/)
}
function shortUpper( words ){
return words.map( word => {
if( word.length < 5 ){
return word.toUpperCase()
} else {
return word
}
})
}
function createText( array ){
return array.join(' ')
}
The client sends in the text and we make our functions work:
Great! The client got what he wanted. The problem is: our workers have to manually take the output of the words and shortUpper functions, carry them to the next function, and turn on the function's engine on. That's a lot of work, can we automate this?
Cue dramatic music
Enter composition
We want the function's outputs to be sent to the next function without having to do it ourselves. Like so:
We read this from left to right, but, as I mentioned earlier, we execute from the inside to the outside:
createText <-- shortUpper <-- words <-- text
We even decide to create a function for this popular demand:
Our company has another popular demand: replacing '.' by '!!!' while making the first character of each word uppercase. We have some functions to handle that:
Cool, we're able to compose functions by combining several smaller functions!
Going nuts!
The company's CEO couldn't be happier. The factory transforms text very fast thanks to composing. But he wants more!
What if we had a function that took all the functions as inputs and just made composition happened by itself? We could call it compose.
The engineers gather up and brainstorm. They decide to experiment with the two products they already have. They come up with this:
Our functions takes all the functions needed as parameters. It returns a function that takes the original value as parameters and return all the functions composed in the proper order. Be careful about the order! We execute from the inside to the outside. The last function you specified will be the first executed. How do the function remembers all the functions specified in the parameters? Closure!!!!
Now, we can compose whatever we want with three or four functions. But the CEO wants something generic.
What if we need to compose only two functions? or five? ten? I don't want an infinite amount of functions sitting in my factory. Can you create one function that just take an arbitrary number of functions and compose?
Finally, the engineers come up with the compose function:
The compose function takes a list of functions as a parameter. We use the rest operator (...) to gather that as an array. We return a function with the original value as parameter. Inside of this function, we create a local copy of the functions array ( how? CLOSUUUUUURE ). Then we call the last function of the array with the output of the last function. pop() returns the last element of the array and removes it from the array. The output of the last listOfFunctions element becomes the input of the next one. When our array is empty, we return the final value.
I mean, this is just amazing. Now we can go absolutely crazy.
Moar examples!!!
I'm just playing around now. But the sky is the limit.
Well, I'll stop there. I want to see how librairies like Ramda implement composition, but this is really a fun way to write code. My implementation is of course only one possibility. You could create a different one. You could implement a pipe functionality ( from right to left )... I'll probably explore that in another article.
Love!