Development

Don’t ‘Object’-ify Me: An Introduction to Functional Programming in JavaScript

30 Mar 2018 9:59am, by

In the beginning, computers were like dinosaurs: enormous in size, but tiny in computational ability.

This brain-the-size-of-a-walnut phenomenon is the reason that functional programming, although the first computer programming paradigm ever created, was eclipsed by the imperative and object-oriented paradigms. Quite simply, early computers had limited processing power and simply could not afford the fact that function calls are more expensive than simple loops. And so for decades functional programming largely lived on in academia, while the rest of us got to wrestle with side effects and (shudder) state.

Well, computers evolved. A lot. And over the past couple years functional programming has been rediscovered by mainstream programmers. All the cool kids are interested in expressing their code in terms of pure functions with bound inputs and explicit outputs. (Sounds kinda hot when you put it that way, actually. No wonder it’s becoming so popular). And JavaScript developers may be the most excited of all, since it turns out that JavaScript actually goes three ways. Beyond the object-oriented and imperative properties we’ve come to enjoy in our love/hate relationship with the world’s most widely-distributed computer programming language, it turns out that JavaScript also can work as an untyped functional programming language.

Actually, ES6 introduced some very functional programming-friendly features like map() and reduce() so it makes sense that functional-curious coders may want to explore more. So, without further ado, here are the haps on getting started with functional programming in JavaScript.

So What Is Functional Programming, Anyway?

Functional programming is a paradigm where functions are king, rather than objects and assignments. It is also a style of coding, of organizing and then writing your code, that is a different way of thinking about how to approach a task or solve a problem.

In functional programming, everything is expressed in terms of functions. (Big surprise, huh?). It is extremely explicit: taking input scoped only to that specific function, and computing and returning output. That’s it. Whereas imperative programming takes a “first take this, then do that” style, promiscuously grabbing variables from all kinds of scope — which can lead to unintended side effects. And in object-oriented you programming, you get the fun of ‘this’ bindings.

Typical imperative function:

A more functional way of doing the same thing is to define a function that takes an input, name, and returns a direct output:

Pure Functions, and a Little on the Side (Effects)

Functional programming is all about being declarative and explicit, and having immutable data. (Immutability is something else ES6 brought to JavaScript with ‘let’ and ‘const’, btw). This helps avoid side effects —  that is, anything a function might do other than taking the inputs you give it, computing, and returning the output. We do this by using pure functions, which is any function that, given the same input, will always return the same output without side effects. (For example: Math.cos(x), a mathematical function “built in” to JaveScript, will always return the same result for the same value of x and is therefore pure).

A pure function does nothing except take its input, use that and only that to compute an output, and then return that output. Mic drop.

In our above simple functional code example, that is not a pure function: it’s using a globally defined variable to calculate its output. Involving something from outside the function in what the function itself does is most impure. (By the way, technically speaking console.log() also fails the purity test, but we forgive that because it’s so useful, and also unlikely to actually screw up our code by producing side effects).

The only thing that matters to dynamite() is the parameter we give it, and the only thing it returns is its output. This means if you want to replace an object in an array, you map() through it (see further down for the functional hitchhiker’s guide to JavaScript’s built-in map() method) — and map() returns a completely new array. You don’t replace the object in the same array, as may happen in imperative programming. End result: your results are always as expected, zero side effects, and debugging just got a lot easier! So, unless you like crying in front of your computer screen…

Functions are First-Class Citizens. Higher-Order Functions Especially So.

The entire reason functional programming works well in this language is because JavaScript treats functions as values. Thus you can assign functions as values and pass them as parameters. You can even return another function as an output, and pass that to yet another function!

Functions that take other functions as inputs are called higher order functions. In the sample code below, note how the vote() function returns another function as output. We can access both of these by assigning the outer, higher-order function to a variable — but our inputs are controlled entirely within the nested functions, and therefore immutable. They are not in global scope:

The reason that our inputs for ‘election’ are not in global scope has to do with closure: the function defined in the closure remembers the code block in which it was created, and thus will take only inputs also scoped to that same block.

Take some time to sit with this. Wrapping your head around the concept of functions returning functions (returning functions) is the key to programming in a functional style. We need higher order functions to be able to avoid tricks we are used to using form other paradigms. for example…

Don’t Iterate

In JavaScript, especially pre-ES6 JavaScript, we are used to iterating over arrays and objects and doing things to all the items within. Good ol’ for() and while() loops, we’re looking at you.

Instead, in functional style, we use higher order functions like map(), reduce() and filter() — which accept as arguments not only the object or array you want to access, but also any function you’d like to then apply to them.

Ladies and gentlemen, I give you the Map Reduce Sandwich:

We all know how to make a sandwich, right? So you have an array, in the form of a list of ingredients. You need to transform each ingredient so that it is ready for the next step of the sandwich-making process — say we want to carve or chop them up. (“Slice” is more what we’d do in real life but I want to avoid any confusion here with the actual JavaScript slice() method).

In imperative JavaScript, we would run a ‘for’ loop: FOR each of these ingredients, DO this:

In functional style, though, we take the map() method and pass it the array of ingredients and the function chopped(), and it returns a new list where everything has been chopped. Yum:

When comparing both, it’s clear that with functional programming our code is composable, cleaner, and written to match the problem we want to solve. Plus, no risk of weird random ingredients crashing our sandwich!

After everything is chopped, we can then use the reduce() method to combine all the items into our delicious sandwich.

And, if you happen to hate tomatoes, there’s the filter() method to keep them out by making sure that only items that are NOT tomatoes get through the filter.

 

Now Don’t Go Changing.

Functional programming is all about avoiding mutability — i.e., don’t go changing objects in place. (We love them just the way they are).  Immutable means data that cannot be altered once in place; we can set it and forget it, and it will never change.

Let’s look at an example. First, mutation (bad!):

We have a ‘snacks’ variable holding tots, quesadilla, and ham — and then we realize that actually, ham should be eggs, so let’s replace index position 2 in ‘snacks’ with ‘eggs.’ This means the ‘snacks’ variable has been changed: i.e., we have changed in place what was stored in the variable. Yep. We mutated the heck out of that data set.

Functional programming avoids changing data in place, inside an object or array, because this can potentially cause a lot of unintended consequences. In fact, it’s quite often the source of serious trouble in object-oriented programming. Because, if you unintentionally change data, the lunch you thought you were dealing with — tots, a quesadilla, some ham — is more like a breakfast, because ham got swapped for eggs when earlier in your code you replaced that array element. This can introduce major head-clutching bugs which are going to be very hard to track down. Think about it:  at one point in your code, ‘snacks’ is the right thing, in another ‘snacks’ is the wrong thing — but they’re both ‘snacks.’

That is the beauty of immutable data in functional programming: keeping track of your snacks:

Taking a functional programming approach by using map(), instead of changing snacks[2] in place we return a whole new array, newSnacks, containing the changed item. This works by feeding a function into map() (<— higher-order functions accept functions as arguments!)  which looks at each snack in the ‘list’ (array). If it’s ham, the function returns eggs instead. Otherwise, it returns the original snack item. Once we assign this newSnacks variable, it holds the tasty treats we expect, while our original snacks list has not changed at all.

Yes, yes, I know it seems trivial… but I promise that data immutability will help you avoid so many headaches. And crying in front of your computer.

We’ve Only Just Begun.

Many more functional programming-friendly — and very sophisticated — JavaScript tools await. For example, currying, which always takes one single argument and returns another function that compares and then returns a single parameter, nesting them until all the arguments have been applied. Partial application is also very handy; with partial, we apply one or more arguments to a higher order function and then return a function that takes the remaining parameters to complete the application. Sweet stuff, but these are more programming approaches than a straightforward application of built-in methods. It’s best to work with the simple core concepts first. Get comfortable with map(), reduce() and filter() before getting fancy with unscripted functional programming. But The New Stack will be back to show you the way when you are ready!

Adopting this approach requires some paradigm shifts in how we understand working with JavaScript, but I genuinely believe it’s worth the effort to learn and apply. Functional programming is making me a better, and also more confident, coder. And now I find it easy to “chunk” one big task/code block into smaller ones, making the code both more reliable and predictable.

The only downside to keep in mind is, once a codebase gets seriously large, it’s possible to start having efficiency issues: function calls are more expensive than those dumbass simple loops… Expensive, but usually worth it.

Feature image by Umanoide on Unsplash.

A newsletter digest of the week’s most important stories & analyses.