Functional Programming and what it realistically means for Javascript developers
January 06, 2018
This post assumes some knowledge of OO and procedural programming paradigms
Introduction
Functional programming has been around in languages such as Lisp and Haskell for many years. In recent times, it’s become popular in Javascript and other web languages because, when done well, it can be concise, perdictable and easy to maintain and test due to it’s declarative nature.
From my experience and discussions with other developers, there are differing opinions on a few key points of functional programming and how it relates to other popular programming paradigms such as Object Oriented and Prodecural. This post will cover a top level view on what I think functional programming realisticly means for Javascript developers, how it can be used day-to-day to gain some of it’s benefits, and some points which I feel are important but not discussed so much from what I’ve seen.
Quick overview of Object Oriented and Functional Programming
Firstly here’s a brief overview of the defining features of functional and object oriented development:
Object oriented
- Structuring procedural code into classes and objects
- Classes and objects are encapsulated
- State/variables are shared within a class which can cause side effects
- Easier to reason about a problem as Objects are split into related “things”
- Imperative view on programming where code is written in terms of goals and what is required
Functional
- Pure functions are encouraged so if the same input is given, the same output will always be returned
- Functions can be used as variables and passed as parameters to other functions
- Combination (or composition) of functions
- Sate is not shared, and all data must be immutable, leading to no side effects so data/state outside a function is not affected
- This leads to functions being able to run independantly of each other and in any order
- Declarative view on programming where code is written in terms of instructions and exactly how data/actions are processed
There are a lot of posts about functional programming and how it’s great because of the reasons mentioned above plus various other reasons I’ve not included, but I won’t regurgetate that content in great detail. After all it’s covered in great detail in many popular places which are all saying the same thing:
Instead, I’ll cover the real basics, and my thoughts which may help people who want to get an understanding of the bigger picture.
Functional vs Object Oriented Programming - real life comparisons
Instead of going into too much detail, here’s 2 code samples - one in OO and one written with FP in mind, showing how the same goal/output can be approached with each programming technique.
// OO
class Food {
// The class of Food contains methods which access and use shared data
constructor(type) {
this.type = type
}
transformToUppercase() {
// the shared data such as the below line means this is an impure function
return this.type.toUpperCase()
}
}
let theFood = new Food("pizza")
console.log(theFood.transformToUppercase())
// FP
let transformToUppercase = (type) => {
// transformToUppercase takes in it's external data as a parameter (type),
// so this is a pure function, which has no side-effects, and is therefore
// easier to test, debug, and reason about
return function () {
return type.toUpperCase()
}
}
let theFood = transformToUppercase("salad")
console.log(theFood())
This is an extremely simple example to illustrate what I think is one of the most important asect of functional programming which is the usage of pure functions. In the object oriented example, the transformToUppercase()
function is impure as it relies on the type
variable which is shared within the Food
class. In turn, this means there’s now mutable data flying around so there’s a chance that all sorts of things can go wrong if variables are re-assigned, for example.
The functional example, however, doesn’t have the shared type
variable, and instead operates as a pure function which takes the argument type
and returns a new function. This means the transformToUppercase()
function doesn’t create side-effects and makes it easier to test and understand what’s going on.
Of course, thats’s not all there is to functional programming. There are a few other defining features and techniques which can be used to make an application in a functional kind of way, and they are used at different levels in different situations, which I think causes confusion when it comes to defining what functional programming actually is.
The key’s to unlock the doors of functional programming
Pure functions
I’ve already mentioned that I consider the most important part of FP is pure functions. Aside from this, there are a few other patterns which are crucial.
Immutable data
Keeping data immutable means ensuring it can not be changed after it’s defined. If data can be changed, it is possible for other functions or parts of code can suffer as a consiquence if they relied on a certain structure or data set. There are libraries like Immutable JS and differing techniques to help keep defined data constant and “frozen”.
Composition
Another crucial part of FP is being able to clearly declare what you want your program to do in terms of functions. Creating small, re-usable functions is key, and composing these functions together to form larger functions is what makes functional programming so flexible. This is done by functions returning other functions, and/or accepting functions as parameters.
Composition can be facilitated with the use of currying, which is where a function is broke down in to a series of functions which are returned from each other based on the arguments suppled to the first function. You may have seen/used function composition whilst using compose()
function from the Redux library.
More about these kep parts of FP are explained in great examples below…
Further excellent examples
I’m a huge fan of learning by example, and I found a resource on Github which does just that for functional programming. I found it hard to find anything like this, and at the beginning I could really have done with this to make the concepts slot into place quicker.
Here’s the link which gives examples of the same program written in many different ways, starting off at OO programming and leading up to hardcore functional programming gradually. It perfectly illustrates some of my key points below.
Chaining vs function composition
It can be confusing when first trying to learn the basics of Functional Programming because almost all examples are using the same ES6 map()
, reduce()
and filter()
functions. What’s more, these functions are used in chains with libraries like Lodash using the _.chain()
function, so how do these compare?
Here’s an example of the Lodash chain using a map()
:
_.chain([1, 2, 3])
.map((x) => [x, x * 2])
.value()
Chaining, like in the example above, is a declarative way to work with Javascript, but not in the same way that traditional functional programming chains functions together with the use of function composition. The example above simply passes the values back in to the next function, whereas functional composition passes functions as parameters to other functions, perhaps using currying, to achieve a similar outcome.
There’s a great article here which covers this in detail, and another one here covering functional composition at a basic level.
The ES6 and Lodash versions of map()
, reduce()
and filter()
are different. They can be chained without the use of Lodash chain, but cannot be composed together using the compose()
type functions such as:
compose(
function1((x) => x),
function2
)(someArray)
It’s worth noting that I find the vast majority of data manipulation can now be done with the ES6 array prototype methods.
The reason is because in order for functions to be composable they need to accept the data set as a parameter to the function as opposed to using the in built prototype functions of Javascript arras like ES6 provides with [].map()
for example.
In order to be truly functional, libraries like Ramda and Lodash/FP exist as they return functions from their built-in versions of map()
, reduce()
etc. which enable them to be composed.
The steep curve to learn functional programming with Javascript
In my opionion, there not enough mention of some key points which I feel are causing the “steep learning curve” for newcomers and keeping people from adopting fucntional programmng in their day-to-day development. Here are these key points:
The declarative nature of FP
Functional programming is widely seen as declarative as application state passes through functions which describe what is happening to the data, not how it’s happening. This often leads to 10 or 20 lines of procedural or imperative code can be expressed in something like 5 or 6 lines using some common utility functions.
A lot of resources covering functional programming cover these utility functions map()
, filter()
, reduce()
, zip()
and concatAll()
, and you may be left with the question “Yeah but what else do I need to know when manipulating data?“. And the answer is not too far from “Nothing!“. These 5 functions cover the vast majority of manipulation needed on collections of data, which, at a very high level, is most of what programming is!
The point I’m making here is that functional programming at a basic level can be seen as no more than the abstraction of data flow into functions which describe how the data is to be processed, making the code readable, promoting the use of pure functions etc.
It depends how far you go…
It’s worth noting that the definition of FP, from my experience, is open to interpretation and preference when it comes to Javascript. My experience with fully functional languages such as Haskall is close to non-existant, so I can’t comment on this too much, but Javascript is neither a fully OO or fully functional language - it just levereges or emulates the features of both and allows the paradigms to be used…
People have strong opinions on this, so I want to clarify that Javascript, in my opinion, leans more towards being an OO language, but isn’t a text-book OO language because of it’s lack of support for real Classes.
… Coupled with the fact that Javascript has the capability and approachability of a few different programming styles, it means you can write Javascript in which ever way feels more comfortable to you.
FP isn’t an all-or-nothing type of thing.
In fact, it makes more sense not to use a pureist view within Javascript specifically (in my opinion), as Javascript isn’t a fully functional language.
There are many different parts of FP which can leveraged at all levels, such as:
- Using pure functions
- Passing functions as parameters
- Using data collection utility functions such as map, filter etc
- Currying
- Functors
- Streams
- Monads
And I think they all play a part in different scenarios. This is really a “jump right in and start devving some FP” type of scenario, and once you do that you’ll see which bits you’re more comfortable doing in a OO or FP way.
OO and FP are not mutually exclusive
I think the reason object oriented programming is so widely used is because it makes code easy to undertand what’s going on and groups “things” logically into classes. This does have huge advantages such as ease of readability namespacing functions, but this isn’t as much of a requirement for functional programming.
When developing with Javascript specifically, I think it makes complete sense to use both techniques together as they are not mutually exclusive. As mentioned earlier, different parts of FP can be used depending on the situation you’re in.
For example, I think a user interface designs (such as a slider, tab bar etc) seem to work well with object orientation as shared state helps here. On the other hand, I’d probably use functional programming techniques for something like manipulating data before or after a database query. Both these techniquest can be used side-by-side in a Javascript class!
You’re probably using functional programming already
There are parts of writing Javascript which natually use functional programming techniques, especially if you’re sticking to the best practices of a framework such as React or Vue. An example of this is React’s immutability best practice when developing larger applications, and when using Redux for state management. React also promotes the use of array map, filter, reduce etc when working with data in the render()
function.
Another example which is a little more framework agnostic is the use of Javascript Promises, which use then()
functions to clearly and purely display the code as opposed to creating callback hell.
If you want to read more on Promises, I have a post on that too which discusses the asynchronous nature of Javascript in general.
Conclusion
I’d say that as JS is neither fully FP or fully OO (from a pureists perspective) that they can both be used. And that’s the beauty of Javascript right? Having the ability to be so multi-faceted and portable?
It all comes down to the situation you’re in, and what you feel most comfortable doing. Having said that, I think to fully understand the landscape and breadth of the language, it does make sense to understand both OO and FP to a certain point, and allow the transfer of skills to be used effectively. From my time doing functional programming it’s introduced me to many concepts I’s never used before.
Senior Engineer at Haven