Daniel Boros
15 Sep 2021
•
10 min read
This tutorial will show you an easy way to learn and use a functional style of programming in Javascript. If you know how to program in Javascript, but never tried functional programming, this is the tutorial for you.
Functional programming is an old way of constructing code, and it has certain advantages and disadvantages. The main idea is to use pure functions only for any and all implementation of the business logic as well as for everything else.
A pure function is one that takes a certain input and from that produces a specific output and always the same output for the same input. In other words in a pure function cannot be affected by a side effect like the ones that are common object oriented programming.
In OO the output of a method is often altered by the state of the object the method was called on. Similarly, variables defined globally or declared in a closure above a function and used by a function would also produce an impure function. With the exception of the function parameters, only constants and other functions can be used in the body of a pure function.
The benefit of functional programming is testability and reliability. Since everything is made from pure functions it is trivially easy to write automated tests for them since pure functions always conform to the de facto structure of tests. There is no need to set up the correct state in which the tests must be run because pure functions don't need the state only the parameters. Rather than quarantining the state as in OO via encapsulation functional programming simply bans side effects altogether.
The downside is that providing the data for a specific function can be much more challenging than in OO where you can access almost anything you might need using the this
keyword. The upside though is that you no longer have ambiguous keywords like this
which can mean anything and everything and are defined by the context rather than you. Another benefit of functional programming is the reusability of code that OO doesn't possess or is very hard to achieve in that paradigm. It should also be noted that some of the most critical infrastructures such as telecommunications are often programmed using a language designed for functional programming simply because the functional code is much less error-prone.
In any sort of functional programming, we write functions and then we create new functions by composing them together. In imperative programming composition is usually in the form of fn1(fn2(fn3(val)))
, but in the functional paradigm we use a different syntax (see later), but in essence, it is the same. Since we are composing functions rather than calling them sequentially controlling the flow of the program becomes difficult. For example inserting a if
else
statement into a composition of functions is not trivial, since you cannot branch inside normally.
In functional programming, there are several structures and functions that are designed to help with branching and other difficulties such as side effects caused by IO. You might have heard some of these like Monads and Functors which are part of the Fantasy Land Specification that most functional programmers use. If you are programming in a functional style as opposed to raw functional programming, then you don't need to know about or understand these constructs that would be a requirement normally.
Since JavaScript is a multi-paradigm language, so you can still use if
else
or closure
and other features of the language instead of composition, but things like this
and OO, in general, should be avoided if possible. In essence not using Monads and similar constructs is what differentiates Functional from Functional Style Programming. There is no hard rule to avoid these constructs and if you find that you are not able to do a function composition where you normally could, chances are you are in need of a Monad or something similar. Therefore, if you do not want to break up your composition into smaller chunks it could be advisable to look them up or create you own flow control constructs.
A function library is generally required for efficient functional programming, and there are a few we could choose from (like Ramda), but my favorite is lodash/fp.
"Why?" you might ask. There are a few reasons:
In general lodash/fp is probably the easiest to use functional library and if you already know lodash it will be pretty easy to memorize the few functions essential for functional programming.
Assuming that you are using NodeJS v16 and Yarn you would normally install lodash like so:
yarn add lodash && yarn add -D @types/lodash
The types are installed only so that your IDE can have better autocomplete and is completely optional. You can use TypeScript for functional programming, but I recommend against it. TypeScript was developed to support OO programming and has difficulty figuring out the type signatures for compositions and there aren't many clean workarounds for when it fails to do so. Therefore, simple JS is the right choice at this moment.
When you are loading the library in NodeJS you should not use the ES loader unless you know what you are doing. If you insist on using ES modules remember that you can always go back to CommonJS modules.
const _ = require('lodash/fp')
you could also import/require
individual functions from the library. It is up to you.
The primary goal is to write simple functions and then composing them together to create more complex functions and then create even more complex functions by composing those as well. Composing functions is similar to method chaining in lodash except that essentially any function can be composed or made composable. It is a much more universal way of gluing instructions together. Consider the following example:
Let's say that we have a list of players in a traditional poker game like so:
const game = {
players: [
{
playerId: 1,
name: 'Tom',
cards: [
{ color: 'diamonds', number: 2 },
{ color: 'clubs', number: 2 },
{ color: 'hearts', number: 2 },
{ color: 'spades', number: 2 },
{ color: 'hearts', number: 10 },
]
},
{
playerId: 2,
name: 'Bob',
cards: [
{ color: 'spades', number: 3 },
{ color: 'spades', number: 4 },
{ color: 'spades', number: 5 },
{ color: 'spades', number: 6 },
{ color: 'spades', number: 7 },
]
}
]
}
Suppose that we want to count the number of spades in the game. How would we do that?
If we are starting from the upmost level, first we would have to get
the players object, then map
the cards in each object so that we will have an array of array of cards. Then we would flatten
this array to a singe level and filter
the spades and finally determine the size
of the resulting array. In lodash/fp we would compose this functions so:
const countSpades = _.compose(
_.size,
_.filter(['color', 'spades']),
_.flatten,
_.map('cards'),
_.get('players'),
)
console.log(countSpades(game)) // 6
Composition invokes functions from the bottom to the top similarly to how regular composition runs from right to the left.
We could use the unix like pipe
to reverse the order in which the functions are called, but I advise against that. This way the function call closest to the function name will be the last one being called meaning that you will be able to tell what the function returns just by looking at the top. In the above case the composed function will return a number.
Counting the number of spades is nice and all, but what if I want to count any color? What if I need a parameter to define the color we are interested in? The easiest way would be by using closure:
function countCardsWithColor (color) {
return _.compose(
_.size,
_.filter(['color', color]),
_.flatten,
_.map('cards'),
_.get('players'),
)
}
console.log(countCardsWithColor('spades')(game)) // 6
We could even add the game as a parameter as a way to clarify what the input of this function would be, like so:
function countCardsWithColor (color, game) {
return _.compose(
_.size,
_.filter(['color', color]),
_.flatten,
_.map('cards'),
_.get('players'),
)(game)
}
console.log(countCardsWithColor('spades', game)) // 6
But now you see we have a problem, because a function with more than one parameter (which is not unheard of) is not composable. Since in a composition one function passes on its returned value to the next function right above it, since only one value can be returned the function receiving it will only get that one as a parameter.
How is that possible, when we are clearly using get, map and filter which require more than one params?
The answer is currying
. In lodash/fp every function is curried meaning that if the function is called without every parameter, then a new function is returned (instead of calling the function) with the supplied parameters that can be called with the remaining params to call the function. This is called partial application
. For example the function get(attribute, object)
is called with the attribute name of 'players' producing a function that will be now composable because it will only need a single param: object. In lodash/fp every function is curried by default, but if we want to create our own composable functions we will need to call the curry
function like so.
const countCardsWithColor = _.curry(function (color, game) {
return _.compose(
_.size,
_.filter(['color', color]),
_.flatten,
_.map('cards'),
_.get('players'),
)(game)
})
console.log(countCardsWithColor('spades')(game)) // 6
Every named (not inline) function you define should be curried. One drawback of curried functions is that they cannot have any optional parameters. This is why many functions in lodash/fp are only called with the absolutely necessary parameters instead of adding all the optional params we are used to from the regular lodash library. Using a fixed number of params is a must in functional programming. Functions like these are commonly referred to as fixed arity
functions.
Now that countCardsWithColor
is curried we could create the previous countSpades by partially applying
the first parameter spades
to it:
const countSpades = countCardsWithColor('spades')
Not all functions need to be curried. For example inline functions probably don't need any currying. Personally I prefer to use the arrow notations for inline functions, because it is shorter. In the filter
of lodash/fp in countCardsWithColor
we could replace ['color', color]
with (card) => card.color === color
(since it is a lodash predicate) like so:
const countCardsWithColor = _.curry(function (color, game) {
return _.compose([
_.size,
_.filter((card) => card.color === color),
...
If you have used lodash before you probably realized that the parameters for filter
, map
and get
in lodash/fp are reversed (not counting the optional parameters that were removed). This is because in functional style programming functions
are usually the first parameter rather than the last, because this makes composition easier. Function variables tend to be defined using const which means they are not likely to change and that makes them partially applicable. However, if you need to partially apply the second or the third parameter instead of the first, that can be done as well. For example if we had to use the old map function of lodash map(collection, iteratee)
instead of map(iteratee, collection)
of lodash/fp, we could achieve the same by placing a _
if we included lodash/fp as the underscore. For example this:
const _ = require('lodash/fp')
const map = _.curry(require('lodash').map)
const { players } = game
console.log(map(_, 'name')(players))
console.log(_.map('name')(players))
Would both log [ 'Tom', 'Bob' ]
. This trick can be quite useful. Consider the possibility that you have an object like a config that cannot be changed (For the sake of simplicity we will use the players
object).
How would you write a function that would find a player object based on his or her name? Since players
is a constant the parameter will be the name
attribute rather than the players
object which we will have to bind to the function somehow.
One simple solution would be this:
const { players } = game
const findUserByName = _.curry(function (name) {
return _.find(['name', name], players)
})
This works, but we could do better than that. We could use the _.matchesProperty
function to match the name like so
return _.find(_.matchesProperty('name', name), players)
Why is this better? You might ask. It isn't, but since matchesProperty
is a function we could partially apply it, and
we can insert a _
as the first parameter where that function will be called. Then we can compose them together:
const findUserByName = _.compose(
_.find(_, players),
_.matchesProperty('name')
)
Doesn't that look better? By doing it like this the players
object is no longer reached using closure, because it is just a simple composition. There are several functions like matchesProperty
in lodash so if you are creative enough you might be able to rewrite many less attractive functions into compositions.
Creating functions via composition is the foundational way functional programming is done, but if you don't need to reuse the function elsewhere you can resolve the function instead of giving it a name. If we only needed, let's say Bob
we could get his player object by:
const playerBob = _.compose(
_.find(_, players),
_.matchesProperty('name')
)('Bob')
This is perfectly fine. You don't need to clutter your namespace with unnecessary names, and you can always convert this to a function if you need to run it multiple times. You can also use this as a value to be returned, if necessary.
Our composed functions may become very complex, and so we might loose track of what's going on. It is important to give our function compositions highly descriptive names. They should be as long as they need to be. Another helpful addition to anyone's library is a logging function, that if added to a composition will log out the current value and then return it as well. This way the rest of the functions can be called as well and logged as well.
const composeLog = _.curry(function (at, obj) {
console.log(at)
console.log(obj)
return obj
})
const countSpades = _.compose(
_.size,
composeLog(4),
_.filter(['color', 'spades']),
composeLog(3),
_.flatten,
composeLog(2),
_.map('cards'),
composeLog(1),
_.get('players'),
)
const fnName = _.compose(...functions)
const fnName = _.curry(function () {})
function
and inline functions using the arrow notationDaniel Boros
I enjoy writing code and it has been a part of my life for so long that, I can hardly imagine living without it. My goal is always to write flawless, optimal, self documenting code that, if I or anyone else will need to change, can do so with ease. I like to improve by coding, with the help of others or without, but nevertheless improve myself whenever possible.
See other articles by Daniel
Ground Floor, Verse Building, 18 Brunswick Place, London, N1 6DZ
108 E 16th Street, New York, NY 10003
Join over 111,000 others and get access to exclusive content, job opportunities and more!