© Anto Aravinth, Srikanth Machiraju 2018
Anto Aravinth and Srikanth MachirajuBeginning Functional JavaScripthttps://doi.org/10.1007/978-1-4842-4087-8_3

3. Higher Order Functions

Anto Aravinth1  and Srikanth Machiraju2
(1)
Chennai, Tamil Nadu, India
(2)
Hyderabad, Andhra Pradesh, India
 

In the previous chapter we saw how to create simple functions in ES8. We also set up our environment to play around with functional programs using a node ecosystem. In fact, we created our first functional program application programming interface (API) called forEach in the previous chapter. There is something special about the forEach function that we developed in Chapter 2. We passed a function itself as an argument to our forEach function. There is no trick involved there; it’s part of the JavaScript specification that a function can be passed as an argument. JavaScript as a language treats functions as data. This is a very powerful concept that allows us to pass functions in place of data. A function that takes another function as its argument is called a higher order function.

We are going to see higher order functions (HOC for short) in this chapter in depth. We start the chapter with a simple example and definition of HOC. Later we provide more real-world examples of how HOC can help a programmer to solve complex problems easily. As before, we will be adding the HOC functions that we are creating in the chapter to our library. Let’s get started!

We will be creating a few higher order functions and adding them to our library. We are doing this to show how things work behind the scenes. The library is good for learning current resources, but they are not production ready for the library, so keep that in mind.

Note

The chapter examples and library source code are in branch chap03. The repo’s URL is: https://github.com/antsmartian/functional-es8.git

Once you check out the code, please check out branch chap03:

...

git checkout -b chap03 origin/chap03

...

For running the codes, as before run:

...

npm run playground

...

Understanding Data

As programmers, we know our programs act on data. Data is something that is very important for the consumption of our written program to execute. Hence almost all programming languages give several data for the programmer to work with. For example, we can store the name of a person in the String data type. JavaScript offers several data types that we cover in the next subsection. At the end of the section, we introduce a solid definition of higher order functions, with simple and concise examples.

Understanding JavaScript Data Types

Every programming language has data types. These data types can hold data and allow our program to act on it. In this brief section, we introduce JavaScript data types.

In a nutshell, JavaScript as a language supports the following data types:
  • Numbers

  • Strings

  • Booleans

  • Objects

  • null

  • undefined

Importantly, we also have our friend functions as a data type in JavaScript language. Because functions are data types like String, we can pass them around, store them in a variable, and so on. Functions are first-class citizens when the language permits them to be used as any other data type; that is, functions can be assigned to variables, passed around as arguments, and returned from other functions similarly as we do for String and Numbers data. In the next section we provide a quick example of what we mean by storing and passing functions around.

Storing a Function

As previously mentioned, functions are nothing but data. Because they are data, we can hold them in a variable! The code in Listing 3-1 is valid code in a JavaScript context.
let fn = () => {}
Listing 3-1

Storing a Function in Variable

In this code snippet, fn is nothing but a variable that is pointing to a data type function. We can quickly check that fn is of type function by running the following code:
typeof fn
=> "function"
Because fn is just a reference to our function, we can call it like this:
fn()

This will execute the function that fn points to.

Passing a Function

As day-to-day JavaScript programmers, we know how to pass data to a function. Consider the following function (Listing 3-2), which takes an argument and logs to console the type of the argument:
let tellType = (arg) => {
        console.log(typeof arg)
}
Listing 3-2

tellType Function

One can pass the argument to the tellType function to see it in action:
let data = 1
tellType(data)
=> number
There is nothing fancy here. As seen in the previous section, we can store even functions in our variable (as functions in JavaScript are data). So how about passing a variable that has reference to a function? Let’s quickly check it:
let dataFn = () => {
        console.log("I'm a function")
}
tellType(dataFn)
=> function
That’s great! Now we will make our tellType execute the passed argument as shown in Listing 3-3 if it is of type function:
var tellType = (arg) => {
   if(typeof arg === "function")
      arg()
   else
          console.log("The passed data is " + arg)
}
Listing 3-3

tellType Executes arg if It Is a Function

Here we are checking whether the passed arg is of type function; if so, call it. Remember if a variable is of type function , it means it has a reference to a function that can be executed. That is the reason we are calling arg() if it enters an if statement in the code in Listing 3-3.

Let’s execute our tellType function by passing our dataFn variable to it:
tellType(dataFn)
=> I'm a function

We have successfully passed a function dataFn to another function tellType , which has executed the passed function. It is that simple.

Returning a Function

We have seen how to pass a function to another function. Because functions are simple data in JavaScript, we can return them from other functions, too (like other data types).

We’ll take a simple example of a function that returns another function as shown in Listing 3-4.
let crazy = () => { return String }
Listing 3-4

Crazy Function Return String

Note

JavaScript has a built-in function called String. We can use this function to create new string values in JavaScript like this:

String("HOC")

=> HOC

Note that our crazy function returns a function reference that is pointing to String function. Let’s call our crazy function:
crazy()
=> String() { [native code] }
As you can see, calling the crazy function returns a String function. Note that it just returns the function reference and does not execute the function. We can hold back the returned function reference and call them like this:
let fn = crazy()
fn("HOC")
=> HOC
or even better like this:
crazy()("HOC")
=> HOC

Note

We use simple documentation on top of all functions that are going to return another function. It will be really helpful going forward as it makes reading the source code easy. For example, the crazy function will be documented like this:

//Fn => String

let crazy = () => { return String }

The Fn => String comment helps the reader understand that crazy function , which executes and returns another function that points to String.

We use these sorts of readable comments in this book.

In these sections we have seen functions that take other functions as their argument and have also seen examples of functions that do not return another function. Now it’s time to bring you to the definition of a higher order function: a function that receives the function as its argument, returns it as output, or both.

Abstraction and Higher Order Functions

We have seen how to create and execute higher order functions. Generally speaking, higher order functions are usually written to abstract common problems. In other words, higher order functions are nothing but defining abstractions.

In this section we discuss the relationship that higher order functions have with the term abstraction.

Abstraction Definitions

Wikipedia helps us by providing this definition of abstraction:
  • In software engineering and computer science, abstraction is a technique for managing complexity of computer systems. It works by establishing a level of complexity on which a person interacts with the system, suppressing the more complex details below the current level. The programmer works with an idealized interface (usually well defined) and can add additional levels of functionality that would otherwise be too complex to handle.

It also includes the following text, which is what we are interested in:
  • For example, a programmer writing code that involves numerical operations may not be interested in the way numbers are represented in the underlying hardware (e.g., whether they’re 16 bit or 32 bit integers), and where those details have been suppressed it can be said that they were abstracted away, leaving simply numbers with which the programmer can work.

This text clearly gives the idea of abstraction. Abstraction allows us to work on the desired goal without worrying about the underlying system concepts.

Abstraction via Higher Order Functions

In this section we will see how higher order functions help us to achieve the abstraction concept we discussed in the previous section. Here is the code snippet of our forEach function defined in Chapter 2 (Listing 2-9):
const forEach = (array,fn) => {
        for(let i=0;array.length;i++)
                fn(array[i])
}

The preceding forEach function here has abstracted away the problem of traversing the array. The user of the forEach API does not need to understand how forEach has implemented the traversing part, thus abstracting away the problem.

Note

In the forEach function , the passed function fn is called with a single argument as the current iteration content of the array, as you can see here:

. . .

fn(array[i])

. . .

So when the user of the forEach function calls it like this:

forEach([1,2,3],(data) => {

//data is passed from forEach function

//to this current function as argument

})

forEach essentially traverses the array. What about traversing a JavaScript object? Traversing a JavaScript object has steps like this:
  1. 1.

    Iterate all the keys of the given object.

     
  2. 2.

    Identify that the key belongs to its own object.

     
  3. 3.

    Get the value of the key if Step 2 is true.

     
Let’s abstract these steps into a higher order function named forEachObject , as shown in Listing 3-5.
const forEachObject = (obj,fn) => {
    for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                //calls the fn with key and value as its argument
                fn(property, obj[property])
            }
    }
}
Listing 3-5

forEachObject Function Definition

Note

forEachObject takes the first argument as a JavaScript object (as obj) and the second argument is a function fn. It traverses the object using the precedng algorithm and calls the fn with key and value as its argument, respectively.

Here they are in action:
let object = {a:1,b:2}
forEachObject(object, (k,v) => console.log(k + ":" + v))
=> a:1
=> b:1

Cool! An important point to note is that both forEach and forEachObject functions are higher order functions, which allow the developer to work on task (by passing the corresponding function), abstracting away the traversing part! Because these traversing functions are being abstracted away, we can test them thoroughly, leading to a concise code base. Let’s implement an abstracted way for handling control flows.

For that, let us create a function called unless. Unless is a simple function that takes a predicate (which should be either true or false); if the predicate is false, call the fn as shown in Listing 3-6.
const unless = (predicate,fn) => {
        if(!predicate)
                fn()
}
Listing 3-6

unless Function Definition

With the unless function in place, we can write a concise piece of code to find the list of even numbers. The code for it looks like this:
forEach([1,2,3,4,5,6,7],(number) => {
        unless((number % 2), () => {
                console.log(number, " is even")
        })
})
This code, when executed, is going to print the following:
2 ' is even'
4 ' is even'
6 ' is even'
In this case we are getting the even numbers from the array list. What if we want to get the list of even numbers from, say, 0 to 100? We cannot use forEach here (of course we can, if we have the array that has [0,1,2.....,100] content). Let’s meet another higher order function called times . Times is yet another simple higher order function that takes the number and calls the passed function as many times as the caller indicates. The times function is shown in Listing 3-7.
const times = (times, fn) => {
  for (var i = 0; i < times; i++)
        fn(i);
}
Listing 3-7

times Function Definition

The times function looks very similar to the forEach function; it’s just that we are operating on a Number rather than an Array. Now with the times function in place, we can go ahead and solve our problem at hand like this:
times(100, function(n) {
  unless(n % 2, function() {
    console.log(n, "is even");
  });
});
That’s going to print our expected answer:
0 'is even'
2 'is even'
4 'is even'
6 'is even'
8 'is even'
10 'is even'
. . .
. . .
94 'is even'
96 'is even'
98 'is even'

With this code we have abstracted away looping, and the condition checks into a simple and concise higher order function!

Having seen a few examples of higher order functions, it’s time to go one step further. In the upcoming section, we will discuss real-world higher order functions and how to create them.

Note

All the higher order functions that we are creating in this chapter will be in the chap03 branch.

Higher Order Functions in the Real World

In this section we will introduce real-world examples of higher order functions. We are going to start with simple higher order functions and slowly move into more complex higher order functions, which are used by JavaScript developers in their day-to-day lives. Excited? So what are you waiting for? Read on.

Note

The examples are continued in the next chapters after we introduce the concept of closures. Most of the higher order functions work with the help of closures.

every Function

Often JavaScript developers need to check if the array of content is a number, custom object, or anything else. We usually use a typical for loop approach to solve these problems, but let’s abstract these away into a function called every. The every function takes two arguments: an array and a function. It checks if all the elements of the array are evaluated to true by the passed function. The implementation looks like Listing 3-8:
const every = (arr,fn) => {
    let result = true;
    for(let i=0;i<arr.length;i++)
       result = result && fn(arr[i])
    return result
}
Listing 3-8

every Function Definition

Here we are simply iterating over the passed array and calling the fn by passing the current content of the array element at the iteration. Note that the passed fn should be returning a Boolean value. Then we use && to make sure all the contents of the array are obeying the criteria that are given by the fn.

We need to quickly check that our every function works fine. Then pass on the array of NaN and pass fn as isNaN, which does check if the given number is NaN or not:
every([NaN, NaN, NaN], isNaN)
=> true
every([NaN, NaN, 4], isNaN)
=> false
Great. The every is a typical higher order function that is easy to implement and it’s very useful too! Before we go further, we need to make ourselves comfortable with the for..of loop. For..of loops can be used to iterate the array elements. Let’s rewrite our every function with a for loop (Listing 3-9).
const every = (arr,fn) => {
    let result = true;
    for(const value of arr)
       result = result && fn(value)
    return result
}
Listing 3-9

every Function with for..of Loop

The for..of loop is just an abstraction over our old for loop. As you can see here, the for..of has eliminated the traversing of an array by hiding the index variable, and so on. We have abstracted away for..of with every. It’s all about abstraction. What if the next version of JavaScript changes the way of for..of? We just need to change it in the every function. This is one of the most important advantages of abstraction.

some Function

Similar to the every function, we also have a function called some . The some works quite the opposite way of the every function such that the some function returns true if either one of the elements in the array returns true for the passed function. The some function is also called as any function . To implement the some function we use || rather than &&, as shown in Listing 3-10.
const some = (arr,fn) => {
    let result = false;
    for(const value of arr)
       result = result || fn(value)
    return result
}
Listing 3-10

some Function Definition

Note

Both every and some functions are inefficient implementations for large arrays as the every function should traverse the array until the first element that doesn’t match the criteria, and the some function should traverse the array only until the first match. Remember that we are trying to understand the concepts of higher order functions in this chapter rather than writing code for efficiency and accuracy.

With the some function in place, we can check its result by passing the arrays like this:
some([NaN,NaN, 4], isNaN)
=>true
some([3,4, 4], isNaN)
=>false

Having seen both some and every function, let’s look at the sort function and how a higher order function plays an important role there.

sort Function

The sort is a built-in function that is available in the Array prototype of JavaScript. Suppose we need to sort a list of fruits:
var fruit = ['cherries', 'apples', 'bananas'];
You can simply call the sort function that is available on the Array prototype:
fruit.sort()
=> ["apples", "bananas", "cherries"]
That’s so simple. The sort function is a higher order function that takes up a function as its argument, which will help the sort function to decide the sorting logic. Simply put, the signature of the sort function looks like this:
arr.sort([compareFunction])

Here the compareFunction is optional. If the compareFunction is not supplied, elements are sorted by converting them to strings and comparing strings in Unicode code point order. You don’t need to worry about Unicode conversion in this section as we are more focused on the higher order functions. The important point to note here is that to compare the element with our own logic while sorting is performed, we need to pass our compareFunction. We can sense how the sort function is designed to be so flexible in such a way that it can sort any data in the JavaScript world, provided we pass a compareFunction. The sort function is flexible due to the nature of higher order functions!

Before writing our compareFunction , let’s see what it should really implement. The compareFunction should implement the logic shown in Listing 3-11 as mentioned at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort .
function compare(a, b) {
  if (a is less than b by some ordering criterion) {
    return -1;
  }
  if (a is greater than b by the ordering criterion) {
    return 1;
  }
  // a must be equal to b
  return 0;
}
Listing 3-11

Skeleton of compare Function

As a simple example, imagine we have a list of people:
var people = [
    {firstname: "aaFirstName", lastname: "cclastName"},
    {firstname: "ccFirstName", lastname: "aalastName"},
    {firstname:"bbFirstName", lastname:"bblastName"}
];
Now we need to sort people using the firstname key in the object, then we need to pass on our own compareFunction like this:
people.sort((a,b) => { return (a.firstname < b.firstname) ? -1 : (a.firstname > b.firstname) ? 1 : 0 })
which is going to return the following data:
 [ { firstname: 'aaFirstName', lastname: 'cclastName' },
  { firstname: 'bbFirstName', lastname: 'bblastName' },
  { firstname: 'ccFirstName', lastname: 'aalastName' } ]
Sorting with respect to lastname looks like this:
people.sort((a,b) => { return (a.lastname < b.lastname) ? -1 : (a.lastname > b.lastname) ? 1 : 0 })
will return:
[ { firstname: 'ccFirstName', lastname: 'aalastName' },
  { firstname: 'bbFirstName', lastname: 'bblastName' },
  { firstname: 'aaFirstName', lastname: 'cclastName' } ]
Hooking again into the logic of compareFunction:
function compare(a, b) {
  if (a is less than b by some ordering criterion) {
    return -1;
  }
  if (a is greater than b by the ordering criterion) {
    return 1;
  }
  // a must be equal to b
  return 0;
}

Having known the algorithm for our compareFunction , can we do it better? Rather than writing the compareFunction every time, can we abstract away this logic into a function? As you can see in the preceding example, we wrote two functions each for comparing firstName and lastName with almost the same duplicate code. Let’s solve this problem with our higher order function. Now the function that we are going to design won’t take a function as its argument but rather return a function. (Remember HOC can also return a function.)

Let’s call this function sortBy, which allows the user to sort the array of objects based on the passed property as shown in Listing 3-12.
const sortBy = (property) => {
    return (a,b) => {
        var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
        return result;
    }
}
Listing 3-12

sortBy Function Definition

The sortBy function takes an argument named property and returns a new function that takes two arguments:
. . .
        return (a,b) => { }
. . .
The returned function has a very simple function body that clearly shows the compareFunction logic:
. . .
(a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
. . .
Imagine we are going to call the function with the property name firstname, and then the function body with the replaced property argument looks like this:
(a,b) => return (a['firstname'] < b['firstname']) ? -1 : (a['firstname'] > b['firstname']) ? 1 : 0;
That’s exactly what we did by manually writing a function. Here is our sortBy function in action:
people.sort(sortBy("firstname"))
will return:
[ { firstname: 'aaFirstName', lastname: 'cclastName' },
  { firstname: 'bbFirstName', lastname: 'bblastName' },
  { firstname: 'ccFirstName', lastname: 'aalastName' } ]
Sorting with respect to lastname looks like this:
people.sort(sortBy("lastname"))
returns:
[ { firstname: 'ccFirstName', lastname: 'aalastName' },
  { firstname: 'bbFirstName', lastname: 'bblastName' },
  { firstname: 'aaFirstName', lastname: 'cclastName' } ]

as before. Wow, that’s truly amazing! The sort function takes the compareFunction, which is returned by the sortBy function! That’s a lot of higher order functions floating around! Again we have abstracted away the logic behind compareFunction, leaving the user to focus on what he or she really needs. After all, a higher order function is all about abstractions.

Pause for a moment here, though, and think about the sortBy function. Remember that our sortBy function takes a property and returns another function. The returned function is what passed as compareFunction to our sort function. The question here is why the returned function carries the property argument value that we have passed.

Welcome to the world of closures! The sortBy function works just because JavaScript supports closures. We need to clearly understand what closures are before we go ahead and write higher order functions. Closures are the topic of the next chapter.

Remember, though, that we will be writing our real-world higher order function after explaining closures in the next chapter!

Summary

We started with simple data types that JavaScript supports. We found that function is also a data type in JavaScript. Thus, we can keep functions in all the places where we can keep our data. In other words, function can be stored, passed, and reassigned like other data types in JavaScript. This extreme feature of JavaScript allows the function to be passed over to another function, which we call a higher order function. Remember that a higher order function is a function that takes another function as its argument or returns a function. We saw a handful of examples in this chapter showcasing how these higher order function concepts help developers to write code that abstracts away the difficult part! We have created and added a few such functions in our own library. We concluded the chapter by mentioning that higher order functions work with the blessing of another important concept in JavaScript called closures, which are the topic of Chapter 4.