Functional Programming in Javascript — Part I

Param Singh
codeburst
Published in
10 min readAug 10, 2020

--

The mystic term of Functional Programming (FP) must be familiar to any JS developer. The first impression when we say “JS supports functional programming paradigm” or “JS is a Functional programming language”, could be, well I use functions in JS, so I’m already doing Functional programming. This is the myth that we’re going to bust together.

You might be using the FP unknowingly but there’s a lot more to the world of Functional programming which takes years of experience to master. It involves re-wiring your thinking process to observe and implement the FP patterns in your style of writing programs.

Preface

Researching online about FP can leave you inundated with mathematical information that you have no awareness of. This is probably why it’s not that popular among the developer community (due to the steep learning curve involved). Even if you learn some of FP concepts in Javascript, like currying or composing, you may find yourself wondering “How the heck can I use it in my real-world projects?” That’s the same situation I found myself in when I learned about currying (which I wrote an article two years back here wherein I explained it with the help of a contrived example). Guess what, the most common question was, “How and when can I use it in the real world? And is that all Functional Programming is all about?” We’ll address all these questions here.

But before we go any further, we need to understand the basic pillars of FP and why is it worth learning FP in general.

What is Functional Programming?

Functional Programming (FP) is a programming paradigm or a way/style of programming which has the following characteristics:

  • We build programs by composing reusable “Pure functions”.
  • Write declarative code instead of imperative.

It’s more of a way of adapting your brain to think of the FP patterns in your programs.

We’ll learn about all of this using practical examples in a moment.

Why Functional Programming?

Below are the convincing reasons to learn FP:

Imperative vs Declarative code

Consider this code snippet which gets the best products out of an array of products.

It does the job, it gives the correct output. But we had to read through the whole code like a story or a series of steps we’re telling the computer to execute in order to make meaning out of it. A computer is excellent at this job of executing programs step by step as a sequence of instructions. And above is a classical example of Procedural programming. So, even though the keyword “function” is used here, it doesn’t make it qualify for Functional Programming because it violates the fundamental principles of FP like pure functions and no side-effects.

Let’s see a declarative version of the above snippet.

Here, we’ve modularized the code well into their individual responsibility pure functions, showing a clear link between their inputs and guaranteed correct output. The code is no longer procedural. We’re able to deduce the outcome of this code by looking at how these pieces of codes or functions are weaved into one another in plain English and returning the outcome in the end.

While imperative code is easier to be understood by machines, declarative code is easier to be understood by humans.

Hence, you’re not only making your code more readable but also reusable by splitting into smaller chunks of individual functionality.

Pure Functions

Continuing to the previous section on modularizing into smaller functions, we’re introducing a high level of confidence in our code. For example: checkIfBestProduct function returns a guaranteed boolean when being passed a product object as an input. And for a given product object, it’ll always give the same output.

A pure function by definition is a function which:

  • Given the same inputs, always returns the same output, and
  • Has no side-effects

For example, values of trigonometry functions like sin, cos, tan for corresponding inputs as angles will always be the same forever, no matter what.

The point is that mathematics is based on pure functions and hence it is provable. If we bring the same principle of Purity in our way of writing programs, it’ll give us a high degree of confidence and predictability and hence lesser bugs.

Consider, this mathematical function:

f(x) = 2x^2 + 3

We know, that given an input of zero, it gives 3, an input of 1 gives 5, an input of 2 gives 11, which is a parabola if we plot it.

In programming, this mathematical function will look like this

function parabola(x) {   return 2 * Math.pow(x, 2)  + 3}

This pure function will give a predictable output and not cause any side-effects. It means that the output produced by this function and anywhere during its execution, it won’t pollute or mutate or affect any other part of the program. It’s completely a neat and isolated piece in itself.

And because it’s pure and doesn’t depend on its lexical scope, it means it’s perfect for Testing, which is another huge advantage. We don’t have to rely on dirty hacks of jasmine or jest mocks and spies here.

expect(parabola(2)).toEqual(11); // Always true and passes

Implementing FP

The essence of FP is how we link together these pure functions as lego blocks to build more functions on top of them to accomplish a task. These are called higher-order functions like:

(f ∘ g)(x) or f(g(x))

If any of the functions isn’t pure, it’ll pollute all its dependencies and make them impure as well.

One way of ensuring whether the function is pure or not is through Referential transparency which means if you can replace a function call with its resulting value without changing the meaning of the program, it’s pure.

Making Functions Pure

Function purity is a paramount requirement for FP. Sometimes, it’s just not possible to have fully pure functions in our real-world application. These are called side-effects. The most common ones are the asynchronous elements like network calls or file i/o operations which are non-deterministic. It means that even adding a console.log statement inside a function technically makes it impure because it’s an i/o operation.

Suppose, we have this impure function as

If any problem happens in the function, for debugging it we’ll have to read the whole function. Although this is a contrived example, consider a huge imperative impure function like this in a large project. It can become really intimidating to debug or manage such a large function body.

Rather, if we could try to reduce the surface area of this function impurity, we could drastically improve the debugging times by simply focusing on the impure areas, because the pure ones would already be tested and proven to generate the correct and guaranteed outputs for given inputs. We could rather focus on the impure areas and save time.

The refactor for this function could look like this:

Here, the lines of code from the getUsers impure function has been reduced by taking out what was possible to be made pure into toJSON, buildUser, and buildUsers functions.

Composition

Another thing that complements well these pure functions is Function Composition or Piping

Consider this contrived example of this imperative functions, wherein each step we’re storing the outcome of an arithmetic operation and passing it as an input to the next step and so on. It’s quite a common pattern to notice in your code and hence might be a good place to start refactoring it to FP as

Composition

We can build a higher-order function that can take functions and its inputs and chain their output together as

If you observe carefully, we constructed our processNumber function from other small functions viz square, double and addOne. This is the real essence of functional programming. We’re writing code as an association of the functions rather than a series of imperative steps. We could reuse the functionality of squaring, doubling or adding a number elsewhere in our application and compose more functions using these. Imagine having this level of flexibility in a larger application project.

Piping

Similar to composing, there exists a pattern called “Pipe” which can relate to Unix Pipe if you’re familiar with it

$ wc -l * | sort -n | head -n 3

The above command gives the file which has the least number of lines among the three files in the current directory.

Here, we’re moving from left to right fashion. We can mimic the same behavior as

Such common functions like pipe and compose are built already by the popular FP libraries like Ramda or lodash/fp and you don’t have to worry about creating them by yourself. In the case of Ramda, these are just two of the functions from a total of 257 functions! We’ll cover some of the prominent ones in part II with some real-world practical examples.

Currying

One of the very popular buzzwords of FP you must’ve heard, most probably in a JS interview. And probably, you know the theory and you can explain someone with the help of this primitive arithmetic example as

const multiply = ( a, b ) => a * b;
const mul = curry(multiply);
mul(2)(3);

The question here is, why would someone want to do this. Passing arguments one by one rather than at once gives the same result, right? It sure does but we learned it because of a JS interview preparation. But now, we will see how useful this function “curry” is (which is named after the American mathematician Haskell Curry by the way).

If you noticed above in all our examples of piping and composition, we have one requirement of the functions being unary, which means they all can accept one input only.

Function with two inputs is called binary, and with n inputs is called n-ary. This can be called as a function Shape.

The shapes of the function being connected or piped need to be identical otherwise they won’t be compatible and don’t fit each other, just like the Lego pieces. Hence, it’ll be a blocker in composing functions out of function and you won’t be able to FP. This is where Currying shines and gives the advantage of modifying the function inputs to help them fit easily with others. Most of the time in a good FP project, you’ll see 95% of unary and binary functions only which makes it very suitable for composition and piping.

Here’s a nice example

We could’ve passed add function directly here to map but because the shape of add being binary is not compatible with the unary callback map accepts, we couldn’t pass it directly.

If we adjust or change the shape of the passed function by currying it and partially passing one of the parameters, we can accomplish it as above.

So, in addition to adapting the shape for compatibility with other functions, there’s one more benefit currying is providing here called as “Partial Application

addToOne function is a specialized version of the more generic add function. It means that one of the parameters is passed and hence this function is partially applied and needs one more argument to give a meaningful output. It’s hard to see the value in this partial application pattern in this small silly example but let's consider this generic ajax ternary function:

We’re creating some specialized methods which can be used frequently in the project by partially applying some of the parameters already, which saves a lot of repetition at each invocation in the codebase.

This can be made easier with Currying as

If you’re curious to know how currying works under the hood, here’s the detailed article discussing all about it.

Summary

These are just the fundamentals of getting started with functional programming to have you onboarded on the idea of FP. There are plenty of mystic mathematical words and jargon out there if you look for FP. The best way to learn is through examples. And, in the sequel to this article, we’ll see how we can use these patterns to convert or start writing more FP code. But until then it’s important to understand the basic principles of FP which we just discussed. There’s a lot more to FP than this.

Stay tuned for the next part.

Thanks. Happy Coding 🤓

--

--

Senior Front-end Engineer at AWS, Ex-Revolut, Flipkart. JS enthusiast. Cynophile. Environmentalist