In the previous chapter we saw two important techniques for functional programming: currying and partial application. We discussed how these two techniques work and that as JavaScript programmers we choose either currying or partial application in our code base. In this chapter we are going to see what functional composition means and its practical use cases.
Functional composition is simply referred to as composition in the functional programming world. We are going to see a bit of theory on the idea of composition and quite a few examples of it, then we will write our own compose function. Understanding how the compose function can be used to writer cleaner JavaScript is a fun task.
Note
The chapter examples and library source code are in branch chap07. The repo’s URL is https://github.com/antsmartian/functional-es8.git .
Once you check out the code, please check out branch chap07:
...
git checkout -b chap07 origin/chap07
...
For running the codes, as before run:
...
npm run playground
...
Composition in General Terms
Before we see what functional composition is all about, let’s step back and understand the idea behind composition. In this section we explore the idea of composition by using a philosophy that is much more pronounced in the Unix world.
Unix Philosophy
Make each program do one thing well. To do a new job, build afresh rather than complicate old programs by adding new “features.”
This is exactly what we are doing as part of creating our functions. Functions, as we have seen until now in this book, are supposed to take an argument and return data. Yes, functional programming does follow Unix philosophy.
Expect the output of every program to become the input to another, as yet unknown, program.
That’s an interesting quote. What does it mean by “Expect the output of every program to become the input to another”? To make the point clear, let’s look at a few commands on a Unix platform that were built by following these philosophies.
Note
Here the content of test.txt will be Hello world.
That’s so simple. Another command called grep allows us to search for content in a given text. An important point to note is that the grep function takes an input and gives the output (again very similar to a function).
Expect the output of every program to become the input to another, as yet unknown, program.
Note
The symbol | is called a pipe symbol. This allows us to combine several functions to create a new function that will help us to solve our problem. Basically | sends the output of a function on the left side as an input to a function on the right side! This process, technically, is called s pipeline.
Expect the output of every program to become the input to another, as yet unknown, program.
As our example shows, the grep command or a function receives the output of a cat command or a function. Here we have created a new function altogether without any effort by combining two existing base functions. Of course, here the | pipe acts as a bridge to connect the given two commands.
Let’s change our problem statement a bit. What if we want to count the number of occurrences of the word world in a given text file? How we can achieve it?
Note
The command wc is used to count the words in a given text. This command is available on all Unix and Linux platforms.
Each base function needs to take an argument and return value.
We would be able to compose a new function with the help of |. As this chapter shows, we will be building our own compose function in JavaScript, which does the same job of | in the Unix and Linux world.
Now we have the idea of composing functions from base functions. The real advantage of composing functions is that we can combine our base function to solve the problem at hand, without re-creating a new function.
Functional Composition
In this section we discuss a use case where functional composition will be useful in the JavaScript world. Stay with us; you’re going to absolutely love the idea of the compose function.
Revisiting map,filter
In Chapter 5, we saw how to chain the data from a map and filter to solve the problem at hand. Let’s quickly revisit the problem and the solution.
Apressbook Object Structure, Let apressBooks = [
Getting author Details Using map
The code to achieve the solution tells an important point. The data from our filter function is passed into the map function as its input argument. Yes, you have guessed it correctly: Does it sound like the same problem we solved in the previous section using | in the Unix world? Can we do the same thing in the JavaScript world? Can we create a function that will combine two functions by sending the output of one function as an input to another function? Yes, we can! Meet the compose function.
compose Function
compose Function Definition
The compose function is simple and does what we need it to do. It takes two functions, a and b, and returns a function that takes one argument c. When we call the compose function by supplying the value of c, it will call the function b with input of c and the output of the function b goes into function a as input. That’s exactly what a compose function definition is.
Now let’s quickly test our compose function with a simple example before we dive into our running example from the previous section.
Note
The compose function executes b first and passes the return value of b as an argument to the function a. The direction of functions invoked in compose is right to left (i.e., b executes first, followed by a).
Playing with the compose Function
With our compose function in place, let’s build some examples.
Imagine we want to round a given number. The number will be a float, so we have to convert that number to a float and then call Math.round.
The output will be 4 as we would expect. As you can see in this example, the data (which is the output of the parseFloat function ) is passed as input to Math.round to get a solution; this is the right problem candidate that our compose function will solve.
What we have just done is functional composition! Yes, we have composed two functions to build a new function on the fly! A key point to note here is that the functions Math.round and parseFloat aren’t executed or run until we call our number function .
The newly created function countWords using compose is an elegant and easy way to author simple functions by composing multiple base functions.
curry and partial to the Rescue
We know that we can compose two functions only if this function takes one input argument. That’s not always the case, though, as there can be functions that have multiple arguments. How are we going to compose those functions? Is there something we can do about it?
Now can we use the compose function to compose both map and filter with specifics to our example? Remember that both map and filter functions take two arguments: The first argument is the array and the second argument is the function to operate on that array. Therefore we cannot compose these two functions directly.
Note
You might be wondering why we have small functions even for simple things. Remember that composition is all about small functions being composed into a larger function. Simple functions are easy to read, test, and maintain; and using compose we can build anything out of it, as we will see in this section.
Let’s take some time to understand the position of the partial function in the current problem domain. As mentioned, the compose function can only compose a function that takes one argument. However, both filter and map take two arguments, so we cannot compose them directly.
Using compose Function
We got back exactly what we wanted without compose, but the latest composed version titleAndAuthorForGoodBooks is much more readable and elegant in our opinion. You can sense the importance of creating small units of function that can be rebuilt using compose as per our needs.
How about getting only author names for books with ratings that equal 5? That should be easy, right? We leave it to you to solve this using the functions already defined and the compose function .
Note
In this section, we have used partial to fill the arguments of a function. However you can use curry to do the same thing. It’s just a matter of choice. Can you come up with a solution for using curry in our example here? (Hint: Reverse the order of argument for map, filter).
compose Many Functions
Currently our version of the compose function only composes two given functions. How about composing three, four, or n number of functions? Sadly, our current implementation doesn’t handle this. Let’s rewrite our compose function so that it can compose multiple functions on the fly.
compose many Function
Note
This function is called composeN in the source code repo.
Note
Recall from the previous chapter that we used the reduce function to reduce the array into a single value (along with an accumulator value; i.e., the third parameter of reduce). For example, to find the sum of the given array, using reduce:
reduce([1,2,3],(acc,it) => it + acc,0)=> 6
Here the array [1,2,3] is reduced into [6]; the accumulator value here is 0.
Here we are first reversing the function array via fns.reverse() and passing the function as (acc, fn) => fn(acc), which is going to call each function one after the other by passing the acc value as its argument. Notably, the initial accumulator value is nothing but a value variable, which will be the first input to our function.
We got back the expected result. Go and play around with our new compose function!
Now we have a solid understanding of how to use the compose function to get what we need. In the next section, we are going to see the same concept of compose in a different way, called pipelines .
Pipelines and Sequence
In the previous section, we saw that the data flow of compose is from left to right, as the functions on the left mostly get executed first, passing on the data to the next function, and so on, until the rightmost function gets executed last.
Certain people prefer the other way—where the rightmost function gets executed first and the leftmost function gets executed last. As you can remember, the data flow on Unix commands when we do | is from right to left. In this section, we are going to implement a new function called pipe that does exactly the same thing as the compose function, but just swaps the data flow.
Note
This process of flowing the data from right to left is called pipelines or even sequences. You can call them either pipeline or sequences as you prefer.
Implementing pipe
pipe Function Definition
That’s it. Note that there is no more call on fns reverse functions as in compose, which means we are going to execute the function order as it is (from left to right).
The result is going to be the exact same; however, notice that we have changed the order of functions when we do piping. First, we call splitIntoSpaces and then count and finally oddOrEven.
Some people (who have knowledge of shell scripting) prefer pipes over compose. It’s just a personal preference and nothing to do with the underlying implementation. The takeaway is that both pipe and compose do the same thing, but with different data flow. You can use either pipe or compose in your code base, but not both, as it can lead to confusion among your team members. Stick to one style of composing.
Odds on Composition
In this section, we discuss two topics. The first is one of the most important properties of compose: Composition is associative. The second discussion is on how we debug when we compose many functions.
Let’s tackle one after the other.
Composition Is Associative
As you can see in these examples, the result is going to be the same for both cases. Thus it proves the functional composition is associative. You might be wondering what the benefit of compose being associative is?
This code is possible just because the composition possesses the associative property. Earlier in the chapter we discussed that creating small functions is the key to composing. Because compose is associative we can create small functions by composition, without any worry, as the result is going to be the same.
The Pipeline Operator
One other way of composing or chaining base functions is by using the pipeline operator. The pipeline operator is like the Unix pipe operator we saw earlier. The new pipeline operator is intended to make the chained JavaScript functions’ code more readable and extendible.
Note
At the time of writing, the pipeline operator is still at Stage 1 draft (proposal) state in the TC39 approval workflow, which means it is not part of the ECMAScript specification yet. The latest status of this proposal along with browser compatibility will be available at https://github.com/tc39/proposals .
Let us see some examples of the pipeline operator.
That is more readable, isn’t it? Of course, it is easier to read than the nested expressions, contains fewer or no parentheses, and has less indentation. Remember at this point it only works on unary functions, functions with only one argument.
Note
We haven’t had the chance to execute it using the Babel compiler at the time of writing because the operator is in proposal state. You can try the preceding example when the proposal passes Stage 0 (Released) using the latest Babel compiler. You can also use an online Babel compiler like the one at https://babeljs.io/ . The latest status of this proposal’s inclusion into ECMAScript can be watched at http://tc39.github.io/proposal-pipeline-operator/ .
Once again, this operator just a syntactic alternative; the code behind the curtains remains the same, so it is a matter of choice for the developer. However, this pattern saves a few keystrokes by eliminating the effort to name intermediate variables. The GitHub repository for this pipeline operator is https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-pipeline-operator .
Note
Here the character _ can be replaced with any valid variable name.
Debugging Using the tap Function
We have used the compose function quite a lot in this chapter. The compose function can compose any number of functions. The data are going to flow from left to right in a chain until the full function list is evaluated. In this section, we teach you a trick that allows you to debug the failures on compose.
That is going to print the input argument that the count function is going to receive. This simple function can be very helpful in debugging what data a function does receive.
Summary
We started this chapter by taking Unix philosophy as an example. We have seen how, by following the Unix philosophy, Unix commands like cat, grep, and wc could be able to compose as needed. We created our own version of the compose function to achieve the same in the JavaScript world. The simple compose function is useful to developers as we can compose complex functions as needed from our well-defined small functions. We also saw an example of how currying helps in functional composition, by a partial function.
We also discussed another function called pipe, which does exactly the same thing but inverts the data flow when compared to the compose function. At the end of the chapter we discussed an important property of compose: Composition is associative. We also introduced the usage of a new pipeline operator (|>) also called the binary infix operator, which can be used with unary functions. The pipeline operator is a proposal to ECMAScript 2017 that is at the proposal stage and will be available soon in the next release of ECMAScript. We also presented a small function called identity that we can use as our debugging tool while facing problems with the compose function.
In the next chapter, we are going to cover functors. Functors are very simple, but very powerful. We introduce use cases and a lot more about functors in the next chapter.