An introduction to functional programming in JavaScript

What is functional programming?

Functional programming (FP) is a coding paradigm which, what may come as a surprise, has its roots in mathematics. But fear not - we will not dwell on the mathematical aspects in this article (or future parts of the series). For now, let’s focus on the core principles that FP relies on:

  • function purity,
  • avoiding side effects,
  • avoiding shared state,
  • immutability.

The short list above should be enough to see that it differs greatly from the more traditional approaches like procedural programming or object-oriented programming. Why is it so and what are the benefits? Let’s dive a bit deeper. 

Sidenote: This topic may seem scary at times and certainly isn’t beginner-friendly. If you ever feel lost, don’t worry! It may take some getting used to, but don’t get discouraged easily.In fact, you have probably used some of the functional concepts in practice, without even knowing it. And if you’re just at the beginning of your developer journey, check our tips about how to start a career as a frontend developer and what should be your first steps to become a Junior Web Developer.

Function purity and avoiding side effects

You’ve probably heard about pure functions all over the place. So what are they? A pure function is a function that produces no side effects. In simple words - it does not modify or rely on any variable outside of its scope, nor does it produce any output beside the value it returns (for example: no logging to the console or making requests). A pure function, given the same inputs, will always return the same output, no matter the context or application state.

Such behavior has its own name - it’s referred to as referential transparency, and is really, really important in understanding functional programming. Let me bring up the definition given by Kyle Simpson in his book Functional-Light JavaScript (which I highly recommend!): 

Referential transparency is the assertion that a function call could be replaced by its output value, and the overall program behavior wouldn't change. In other words, it would be impossible to tell from the program's execution whether the function call was made or its return value was inlined in place of the function call.

Avoiding side effects by using pure functions wherever possible makes our code easier to reason about - we don’t need to worry about the context at all! The result of any function depends solely on the arguments passed in. Needless to say, it makes writing tests a lot easier too.

Avoiding shared state

This may seem counterintuitive to developers used to the object-oriented approach, but functional programmers will try to avoid using any shared state whenever possible. Instead, we will try to rely on deriving and calculating new data every time.

But why? What’s the problem with shared state?
The main downside to using any form of shared state is the effort we need to put in in order to fully understand the effects of any function call. To predict the outcome and next state value, we would need to know about all previous function calls that modified any of the shared variables. That seems like a lot of work, doesn’t it? By getting rid of shared state, we can avoid a lot of unnecessary mind work and hard to catch bugs.

Immutability

In simple words, immutability means not making any changes to an object after it has been created. Functional programming greatly encourages us to mutate data as little as possible. Instead, we should always derive new data from the old, for example by copying it into another variable. Although it seems straightforward at first glance, it may be tricky to avoid mutating values at times. We will dig deeper into the common gotchas and why it’s so important in a future part of the series.

What makes JavaScript suitable for Functional Programming?

Unlike languages like Haskell, Elm or Clojure, JavaScript was not written with functional programming in mind. Nevertheless, there are some features that make FP possible, and one of them are first class functions. In other words, functions act as any other type of variable - they can be held by another variables, passed to other functions as parameters or be returned by them. Combining first class functions with closures (the mechanism allowing functions to “remember” the context they were defined in) allows us to use higher order functions (functions that return or accept other functions as parameters) and truly take advantage of many abstractions that FP provides.

Despite JavaScript not having any functional programming utilities in the standard library, the community has (as always) come up with some great ones that we can and will use. The most popular examples are Ramda and Lodash/FP.

If the things mentioned above are new to you, I’d recommend reading the "Scope and Closures" part of the “You Don’t Know JS” book series (again by Kyle Simpson) and Higher Order Functions chapter from the “Eloquent JavaScript” book by Marijn Haverbeke before diving deeper into the topic.

Is it worth it - pros and cons of Functional Programming?

The downsides

Functional programming takes some time getting used to. Some of its concepts (like recursion) require a change in the way we think about our code, and thus can be problematic. The terminology used in FP may be intimidating at times, too. These things tend to discourage many developers, especially at the beginning.
Also, avoiding mutations can sometimes negatively affect performance, for example by copying data instead of mutating it in place, thus using more memory.

The upsides

The most important advantage is the understandability of the functional code. By focusing on being declarative, we signal to the user what we want to do, instead of just telling the computer how to do it. 
What’s more, when knowing that a function is pure, we can feel safer when using it, and do not need to worry about potential unexpected bugs it could cause in another part of the program. It makes the code much more testable and maintainable.
The performance issues mentioned above may never even be visible by a human eye (and probably never will in most applications you will write). Of course, if you encounter any performance-related problems, consider switching to another paradigm. You don’t have to write fully functional code everywhere :)

Another advantage may be the fact that more and more JS tools are adopting functional programming. If you’re familiar with React, you probably heard of higher order components. Another widely known example would be Redux - its core principles align very well with FP principles we’ve just talked about. For example, it requires using pure reducers and immutability, and encourages the use of concepts like composition and higher order functions. You may have also heard of RxJS, a functional reactive programming library worth its own article series.

Wrapping it up

Our brief introduction into the topic of functional programming has come to an end. 
In the future parts of the series, we will get to know the basics of some FP concepts in practice. There will finally be some code! We will be using some of the newest ES6+ features (for example arrow functions and rest/spread), so if you’re not familiar with them already, be sure to get up to speed (for example by reading the "ES6 & Beyond" part of “You Don’t Know JS” series). What’s more, we will make frequent use of closures, higher order functions and array operations like map or reduce (assuming you’re familiar with them, so catch up if you’re not :)). It will start to get more and more exciting from now on, so stay tuned for the next part!

Like what our developers do? Join our frontend team, we’re looking for software developers - check our job offers!  

Navigate the changing IT landscape

Some highlighted content that we want to draw attention to to link to our other resources. It usually contains a link .