Introduction to functional JavaScript programming

Dominik Freier
Sep 26, 2019 · 9 min read

This is an introduction to functional JavaScript programming. First, you will learn what functional programming is about and what pure functions are. Afterwards I explain two built-in array functions. Don’t miss the section about currying and point-free notation. Make a couple of unit tests pass, right within this article! The awesome Ramda library will help a lot.

Edit: This is an article I published on my personal blog in January 2017. I decided to shut the blog down, but I wanted to keep this article for you. So here you go.


The main concepts

Before you start using functional JavaScript programming in your projects, it’s important to understand what the differences to imperative and object oriented programming paradigms are. It’s possible to use many of these paradigms in the same project. But you should have a clear understanding when to use which one of them and when not to.

As you read this article, you might think “Hey, that’s nice in theory. But I don’t see how that helps me in practice.”. I kindly ask for a little bit patience. I’ll have to introduce a few basic tools and concepts first. And they are easier to explain with trivial samples. I tried to make up some more realistic ones at the end. You are invited to play around with them.

What object-oriented programming is about

When looking at any programming language, there are two major building blocks: Data and behaviour. Object-oriented programming is quite opinionated on these two. Both, data and behaviour are encapsulated within an object. Focus is on the behaviour.

Take a Person object for example. It typically consists of a first name, last name and a date of birth. With just these three properties, the object is nothing but a plain key-value data structure.

Let’s make these three properties private. And instead add two public methods getAge() and getDisplayName(). Now the Person object has behaviour that gives us an advantage over a plain data structure. The behaviour is how to calculate the current age and display name. All based on the underlying data.

What functional JavaScript programming is about

Functional programming has another approach to deal with data and behaviour. It keeps them very separate. In functional JavaScript programming, objects are plain key-value data structures. Without any context or behaviour. And functions are the composable pieces of behaviour operating on that data.

Smart people have recognized patterns in low level problems, which are solved over and over again. These problems can be solved once, with very specific functions. We call them higher-order functions.

The power of functional programming kicks in when we notice that we can solve another problem by composing existing functions. And even the task of composing functions is a problem that can be solved with — you guessed it — another higher-order function! Crazy, right?

Pure functions

To make all this work, every function (especially higher-order functions) has to follow a set of rules. We need them in order to guarantee the functions to work under all conditions. A function which corresponds to these rules is called a pure function.

So what are these rules for pure functions? Well, to explain that, we go back to school for a moment.

Remember the functions you learned about in math lessons? For example the square function. For every defined input of this function, there is exactly one output. You would expect the square function to always return the number 4 when you passed in 2. No matter if it's rainy or sunny outside.

And there we have our first rule:

The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices […] Wikipedia

So the first rules says that nothing from outside the function has any effect on the function evaluation. The second rule is the exact complement. The function evaluation has no effect on anything outside:

Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices […] Wikipedia

What you’ve learned so far

You learned what makes functional JavaScript programming different from the object-oriented style. Keep the following in mind:

  • Data and behaviour are kept separate
  • Functions focus on small, recurring problems
  • Compose functions to solve harder problems
  • They have to be pure: Same input means same output and no side effects on anything outside

Built-in array functions

Maybe you’re already using functional programming concepts in your everyday JavaScript projects. I have picked two widely used higher-order functions I want to explain in detail: map and reduce.

Map

A very useful function is Array.prototype.map(). It transforms each element of an array into something else. The map function receives a parameter which is a callback function.

The map function iterates over each element and invokes the callback with the current element as an argument. The returned value of that callback will be included in the resulting array. The callback is a pure function with one parameter. Such functions are called unary functions.

Notice that the map function is a pure function as well. It doesn't modify the original array, instead it returns a new one containing the mapped elements.

So far so good. Read the last line loud: “Abbreviations equals persons map to short name”. Thats pretty much what it does. Reads a lot nicer than a for loop, doesn’t it?

Reduce

Okay, map was easy. This one is a little tougher. But actually it's not so different from map.

We use the reduce function when we want to aggregate all values of an array. I mean bringing it down to only one single value. When using reduce, we have to pass a special kind of function too. This time it is called an accumulator. Accumulators have two parameters and therefore belong to the group of binary functions. The first argument is an intermediate state (I will get on that in a moment). And the second one is the current element in the iteration over the array's elements.

When the reduce function does it's job of aggregating the array's elements, it can only handle one element after another. The reduce function's implementation keeps track of an intermediate state.

The state builds up with each iteration. It is the value that the reduce function returns at the end. The initial value of this intermediate state is an optional parameter of the reduce function.

Wait a second. Reduce? State? If you know Redux, I’m sure this does ring a bell. A Redux reducer is absolutely the same as an accumulator. Instead of aggregating an array of elements, a Redux reducer aggregates a stream of actions. Thats it.

Map is Reduce

Huh, map is reduce? Yes, almost. It is reduce with an empty array as initial value and a modified callback function. Let's look at that in code:

As you can see, map does nothing more than building the accumulator function for you and calling reduce with an empty array as initial value. The accumulator function calls the callback and concatenates the result with the intermediate state.


Function composition

As I said above, map and reduce are higher-order functions. But the way they are built into the JavaScript language is not truly functional. Remember, functional programming makes a strong distinction between data and behaviour.

In the examples above, map and reduce are called on the array instances directly. This mixes data and behaviour. It's not what we want when doing functional JavaScript programming.

Point-free notation

Point-free notation means that we are writing code without . (dots). In object-oriented programming the object is the "thing" that comes first. But in the functional world, we care the most about functions. The data is the thing that we know last. We know it when we call the function at runtime.

Let me illustrate that with a math example. We have function f(x) and function g(x). When we define a function h which should do g(f(x)), then we can define that in a more declarative way: h = f ∘ g. See how x ("the data") disappears?

Okay, I said adding map as a function on Array.prototype is not truly functional. But how is it done right? Let's assume the implementation of Array.prototype.map is done well. We just want to change the way it is called. Let's create a few functions we can use in a functional way:

Don’t use this code in your projects, it’s only here for demonstration purpose.

Compose

Remember, a fundamental idea behind functional programming is to write small functions once, compose them to bigger functions when needed. The compose function is a higher-order function.

It takes two (or more) functions and returns a new function. It builds kind of a pipe. The first functions result is the input for the second function.

Take a look at the function definition and make sure you really understand it. Especially the fact that the compose function returns a function again is key. Also notice that the evaluation goes from right to left!

This point-free (and also quite pointless) example illustrates the usage of compose. Notice that the data parameter ([1,2,3] in this case) is put at the end. This is very important. It is a requirement to be able to do composition when dealing with functions that take more than one argument. This is when currying comes into play.


Currying

When creating function compositions, you will quickly face a problem. The first function in the “pipeline” can take as many arguments as you want. But since it returns exactly one value, all the following functions have to be unary. Sometimes we want to use a function with more than one parameter in this pipeline. This code example shows what I mean:

I solved the problem here by wrapping the function into another function. And this is what currying is all about. The arguments which are known right now can be baked into the new function. In this case the number 2.

Wrapping the function every time we want to use it that way is not very handy. Is there maybe a way to modify the function itself in order to support this?

Yes, there is a way. Take a look at the add function. To do its calculation, we have to pass both arguments at the same time. But what we want is to pass one argument now and the second one later.

Hey, that’s exactly what we wanted! When we call add(2), a unary function is returned, ready to take the second argument! But.. There is still something I don't like. When I want to call add with both arguments right now, I have to do it like so: add(1)(1). Urgh!

Now, ladies and gentlemen, drum-rolls for.. Another higher-order function! This times it’s called curry. Pass any function with two or more arguments to curry. The result is a modified version of it. When called with all arguments, it's invoked immediately. When called with fewer argument, the function caches them. Until the final argument arrives.

Phew! That was a lot! But now comes the fun part.


Play with Ramda

Ramda is a very well designed library for functional JavaScript programming. All functions (with arity > 1) are curried by default. And they are all designed for composition and point-free notation. So the data comes last. But more importantly, authors have added tons of tiny little functions for almost anything. Explore the documentation and play with their repl.

Exercises

As promised, here are some tests cases you can try to make pass. All Ramda functions are available on the global R variable.

Hints

It’s possible to solve all exercises without any explicit function declaration. Compose the following functions to get the job done:

  • R.map
  • R.prop
  • R.groupWith
  • R.equals
  • R.compose
  • R.length
  • R.filter
  • R.flatten

Compare your solutions with mine: http://jsbin.com/xevewiv/1/edit?js,output

Final words

I hope this article helped you to get a basic understanding of functional JavaScript programming. You learned what makes functional JavaScript programming different from the object-oriented style.

Pure functions are the small, testable and reusable building blocks. You saw how easy it is to compose functions when you put the data last. Currying helps to transform functions into more specific versions of them.

This introduction only scratched the surface. There is so much more to reveal in functional JavaScript programming. If you’re curious enough, check out why JavaScript arrays belong to the category of Functors. Write fault-tolerant code using Maybe and Either. Separate pure from impure code using Future and IO.

What do you think?

Let me know what you think about functional JavaScript programming. Did you like this article? Tell me about your prerequisites on functional JavaScript programming. Is there anything you want to have explained in more detail or do you wish to see more realistic examples? An active discussion would be a great pleasure to me.

Resources

Some resources that helped me getting started with functional JavaScript programming:

Have fun :)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade