But what does all this nonsense of first class citizenship and higher-order functions means? Why should you care about functional programming in the first place? And moreover… What the heck is functional programming?
What is Functional Programming?
Functional programming is a paradigm where programs are written by building, composing and evaluating functions as if they were mathematical functions. Special functions which are often characterized by the avoidance of changing state, side-effects and using immutable data.
Why Should You Care About Functional Programming?
The application of functional programming results in a very declarative style of coding where you encapsulate every bit on computation in reusable functions and then combine them together to build greater abstractions and achieve more complex tasks. As such, functional programming style code is very concise and easy to read.
Because functional programming encourages the use of pure functions, deterministic functions with no side-effects whose outputs are totally determined by their inputs, programs written following these principles are very testable and less bug prone. Additionally, following the logic and predicting the behavior of your program becomes much easier.
Functional programming also excels at turning implicit requirements or code contracts into explicit programmatic requirements which help clarify the intention of your code and makes it more readable and easier to understand. In fact, one could say that functional programming is the art of turning the implicit into something explicit.
In summary, functional programming helps you:
- Write reusable code
- Write concise, readable code
- Write less bug prone code that is easier to test
- Make your program behavior easier to understand and predict
- Make your code more intentional by turning implicit requirements into explicit ones
Sounds interesting? Then let’s get started in the path of the functional programeer.
In the next few sections we will go through different functional programming concepts that are often thrown around willy-nilly when talking about functional programming and which you may not be very comfortable or familiar with. So we will help you become more comfortable in this new terrain and explain concepts like pure functions, higher-order functions, closures, etc… using simple examples so you can get a good foundation and gain enough confidence to start writing functional flavored code right away.
Functions As First Class Citizens
You can experiment with all examples in this article directly within this jsBin or downloading the source code from GitHub.
A language is said to have first-class functions or functions as fist class citizens when functions behave like any normal value within the language, that is:
- you can store them in a variable
- you can pass them as an argument to other functions
- you can return them from other functions
This means that functions are not a special mystical construct that can only be defined within the context of a class, but a fundamental construct in their own right that can be used anywhere within a program.
1 2 3
You can pass it as an argument to another function:
1 2 3 4
Where the 1
reduce function performs the addition (via
add) of each item within the array.
And finally you can return a function from another function:
1 2 3 4
In this example, we create a more specific addition function where we fix one of the arguments. The
addX function effectively works as a factory for other functions.
Arrow functions used in this fashion can be a little bit daunting or hard to compute when you are not familiar with functional programming. But if we expand them to look like classical anonymous function expressions you’ll be able to appreciate the returning function more clearly:
1 2 3 4 5 6 7
Functions as first class citizens are a necessity for functional programming because they are a fundamental piece in being able to define higher-order functions.
What are Higher-order Functions?
A Higher-order function (also known as functional form or functor) is a function that has at least one of the following characteristics:
- It takes one or several functions as an argument
- It returns a function
All the other functions that you are familiar with are what we know as first-order functions.
You’ve already seen higher-order functions in action in the previous section. For instance, the
Array.prototype.reduce function is a higher-order function because it takes a function as an argument. A naive implementation of reduce could look like this:
1 2 3 4 5 6
Where we iterate over the elements of an
array and apply the
reducingFunction on each one of them:
1 2 3
reduce doesn’t only work for adding an array of numbers, it’s the generalization of the idea of reducing a collection of values applying a reducing operation into a different value. We can use it for anything! Anything!?
For instance, finding the cheapest wand in a magic shop:
1 2 3 4 5 6 7 8 9 10
Or to filter wands under a given price:
1 2 3 4 5 6 7 8
In the previous section, you also saw an example of a function that returns another function with
addXX. That means that we can jump directly to the next functional programming concept: Pure functions!
A pure function is a completely deterministic function that has the following two characteristics:
- Its result is only determined by its input values and is not affected by any state external to the function
- It doesn’t have any side effects, that is, it doesn’t affect (change, mutate) any state external to the function
Because of these characteristics pure functions only operate on their arguments which makes them very easy to reason about, very testable and less error-prone. Following the same reasoning, programs built with pure functions share the same characteristics.
We already saw several pure functions in this article. The
add function, for example, is a pure function:
The result of adding
b only depends on these inputs and it doesn’t matter how many times you add
1+1 it’s always going to be
2. Additionally, the function has no side effects, it doesn’t affect anything outside its boundaries.
On the other side of the fence we have impure functions which are very common in OOP languages. For instance, imagine that we have a
WizardryShop class that represents a wizardry shop with all its wares, and bills and daily sales.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
computeDailySales of a hypothetical wizardry shop is impure. It depends on the
date argument but it does also depend on the state outside of the function. For any given date the result of computing daily sales will be different based on the state of the
1 2 3 4 5 6 7
Why is this a problem from a functional programming perspective? Well, having an external dependency makes the function implementation more complex, it is harder to predict its behavior and harder to test, as we need to recreate the state for the complete wizardry shop object before we can test it.
Functional programming which comes from lambda calculus and the field of mathematics has a bias for using pure functions which is how functions work in mathematics and which is something good if you want to build robust programs.
It does have impure functions ocassionally though. For instance, the function returned by
addXX is an impure function:
1 2 3 4 5
That’s because the internal function has access to the
x variable defined outside its boundaries. This is what we call a closure and is another tool often used in functional programming.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
In this example we have a higher-order function
createSubstractX that we can use to create other functions that substract a fixed number. The function
substractX is a closure because it encloses the free variable
x that lives in its lexical scope (just one level up).
The interesting thing about closures is that they have access to the enclosed variables even when the closure function is invoked outside their scope. The value of these enclosed variables will be set when the
closure is created:
1 2 3 4 5 6 7
Now that you know about closures, let’s look at immutability!
But What is The Closure exactly?
In the wild, you’ll hear the term closure applied to several concepts: the function that encloses free variables, the technique of enclosing variables, the whole set of function and free variables that are enclosed, the wrapping function, etc.
The formal definition is that a closure is a function that captures the bindings of free variables in its lexical context. So in the previous example that would be
If you reflect about the different concepts that we have discussed so far you’ll realize that functional programming advocates strongly for a very deterministic and predictive style of programming where function results only depend on their inputs and where there are no side-effects. Another concept very natural to functional programming and in the same vein of pure functions is the concept of immutability or immutable data.
Immutable data is data that cannot be changed after it’s been created. The use of immutable data in your programs is yet another way to reduce coupling and side-effects. If your data cannot be changed after creation you are going to be a 100% sure that no function is going to alter that data and therefore your program will be easier to follow and less error-prone, particularly, from subtle bugs caused by coupling.
For instance, imagine that we have the
WizardryShop from a previous example and we add a constructor so that you can initialize the shop with diverse wares and products:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
Now we create two shops located in different parts of a hypothetical city:
1 2 3 4 5 6 7 8
We have a hero that is in bad need of some healing potions so he goes around the city meaning to buy every single potion of greater healing there is. So he goes to the first shop and buys the potion:
Everything is well and fine but when he goes to the second shop with the intent to buy the same potion:
1 2 3 4 5 6
Everything crashes and burns. Both shops had access to the same mutable array and were inadvertedly coupled. We could’ve had a horrible bug present in our codebase for months and we wouldn’t have even noticed it! It would have slowly degraded the economy of our virtual kingdom and created a lot of unrest in the magic item selling community who would have thought that they were being plagued by thieves or evil curses.
If we had decided, however, to use an immutable data structure by using the
Object.freeze method to make our array immutable:
1 2 3 4 5 6
Then when we attempted to mutate the shared array:
1 2 3 4 5 6 7 8 9 10 11 12 13
We would have got alerted as to the fact that both shops are trying to mutate the same data structure. This would have helped prevent the nasty bug we were discussing above and let us take additional measures to ensure that mutation is encapsulated within the shop itself. For instance, it would point us to the necessity of cloning the starting array of items:
1 2 3 4 5 6 7 8 9 10 11 12
Functional programming is a paradigm that seeks to simplify your programs by adding predictability and making them more readable using a declarative style that focuses on what and not on how.
Functional programming revolves around the idea of solving programmatic problems by creating abstractions through functions, and then combining these functions to create yet more high level abstractions and solve more challenging problems. As such, it relies heavily on the idea of functions as first class citizens of a language, functions that can be stored in a variable, or passed, or returned from another function.
Functions that get one or more functions as argument and/or return another function are known as higher-order functions. We saw a couple of examples of this type of functions with
Pure functions are also an important concept in functional programming. They are functions whose result is only determined by their inputs, and which have no side-effects. The use of this type of functions results in a very easy-to-follow code base that is very testable.
Related to the concept of purity is the idea of immutable data. Using Immutable data gives you the assurance that no function is going to tamper with your data and therefore reduce side-effects and coupling. We illustrated this coupling problem with two magic shops which inventories were initialized with the same array.
Finally we discussed closures, as a common technique used with higher-order functions. Closures are functions that capture the value of free variables that live within their lexical scope.
Buy the Book!
Have a great week ahead!! :)
More Articles in These Series
- Object Oriented Programming
- White Tower Summoning Enhanced: The Marvels of ES6 Classes
- Black Tower Summoning: Object Composition with Mixins
- Data Structures
- Functional Programming
- And more stuff to be shaped out:
- Asynchronous Programming
Also commonly known as
foldin functional programming circles and as