Welcome to the Upside Down
Before we get started, there’s something you need to know … If you’ve ever programmed in JS you’ve probably used FP patterns before! These patterns and paradigms have been there all along, we just haven’t been able to see them properly. We are going to start from the familiar and explore new territory. Things may get a bit … well … strange. But fear not! Together we will survive!
First Class Functions
A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.
Functions as constants
In the following example we will declare a const and assign it an anonymous arrow functions.
After the initial assignment constFunction is a constant with a value of a function. We verify that by logging the constFunction variable in the Chrome inspector. Because constFunction is a function we can also invoke it.
Functions as values of keys of an object
Now that we understand that variables can hold functions, let’s demonstrate a function as a value of a key in an object. This should be familiar for anyone who has done any object oriented programming before.
Functions as array items
When functions are first class objects we can pass them as data to an array, just like any other data type. Let’s use the Chrome console and check this out.
Higher order functions
Now that we’ve warmed up, let’s get to the interesting stuff :) JS developers see functions that accept other functions as arguments on a daily basis. If you’re coming from a language that doesn’t support FP this should seem a bit weird 😳😳😳😳😳😳😳 Let’s acquaint ourselves with this concept by looking at some examples.
An asynchronous function that accepts a callback function.
We’re using the jsonfile npm module in this example for the writeFile method. The third parameter that writeFile is expecting is a function. When the jsonfile.writeFile method executes it will either succeed or fail. If it fails it will execute the errorLoggerFunction. Alternatively, we could have gone for a more terse syntax, and dropped the named function:
This example shows the built in asynchronous setTimeout method which accepts 2 arguments. Let’s formalize this a little bit and explain the setTimeout function in functional programming terms.
Let’s start by reading the signature of the function. We can observe that the number of arguments that setTimeout takes is two. In functional programming the number of arguments a function takes is called its Arity, from words like unary, binary, ternary etc. So we can say that setTimeout is of arity 2, or equivalently say that is a binary function.
The arguments that setTimeout expects is a function and a time interval to wait before executing the given function. Hmmm … another function that accepts a function as input?
In functional programming this is so common that these types of functions even have a name! They are called higher order functions.
A higher order function is a function that takes a function as an argument, or returns a function.
There you go. Now you can drop this term low key in any random conversation at work / with friends and sound like a boss! 😂😂😂
Let’s get a little funkier and create an array (list) of functions in the next example.
On line 5 we are declaring an array of functions. We are then using the forEach method to iterate over the array. forEach is a natively supported ES6+ function, that accepts a function to execute on every item in the array. Therefore, forEach is also a higher order function!
Our forEach accepts an anonymous function as input. forEach will iterate over the array and implicitly access the current item in the array and call it getCalculation. It is worth noting that forEach implicitly accesses array elements, in comparison to how we would have accessed the current element if we had used a regular for loop — ie. arrayOfFunctions[i]. Every item in our array is a function, therefore we invoke getCalculation with the arguments that it is expecting.
Fantastic. This example illustrates that functions in functional programming can be passed into arrays (lists) just like any other data type. Functions can go anywhere!
Now let’s build our own higher order function!
The addWrapper function returns a simple addition function when called. By invoking the result of the addWrapper function and supplying it two arguments, we have access to the anonymous addition function.
We could get even crazier with our level of indirection and write a function that returns a function, that in turn returns its own function!
This is a very powerful pattern in functional programming. We will explore it in depth in the coming posts when we talk about currying and partial applications.
First class functions are the cornerstones of any functional programming language. The main point that you should take away from our discussion about first class functions is that functions can be assigned as constants, variables, placed as array elements and even set as values of keys on an object. Additionally, (and most importantly ?!) functions can be returned to and from functions — just like any other data type!
Check out the next post, where we discuss pure functions in JS, why they will make your codebase cleaner and how you can start utilizing them immediately.