Functional Programming for the Dysfunctional

What is Functional Programming and how do I do it?

Statuscode
Published in
8 min readFeb 19, 2017

--

Not so long ago I couldn’t answer the above question.

I had heard that JavaScript is a functional language, but beyond that I was basically: ¯\_(ツ)_/¯.

Since then some time has passed, I’ve worked extensively with a pure functional language, and now it’s time to pay the knowledge forward.

What is Functional Programming?

There are three characteristics of Functional Programming that stand out the most.

  1. Higher Order Functions
  2. Pure Functions
  3. Immutable Data

These are described in more detail below, but first it’s worth pointing out the differences in approaches between Functional Programming (FP) and Object Oriented Programming (OOP), a paradigm you might be more familiar with.

Contrasting FP with OOP

Object Oriented Programming is a paradigm where developers create and use anthropomorphized objects. Instances of these objects are treated as if they have agency, which is to say these are containers of data with their own behavior, and perhaps they inherit data and behavior from elsewhere.

My description of OOP is foreign to most, but I phrased it in that way for a reason. In Object Oriented Programming data and behavior are complected (more on that here). Data and behavior are fundamentally intertwined within instances of objects. In actuality, both data and behavior are also complected with time. In OOP it is very easy to intertwine the what, the how, and the when in a complex way.

Object Oriented Programming complects data, behavior, and time.

By contrast, Functional Programming is fundamentally about separating data from behavior.

Data is just data. It doesn’t have methods. It doesn’t have behaviors. It doesn’t have custom getters or setters. It’s just raw bits in the form of JSON or something similar to JSON.

This data is passed around to various functions and to ensure that the data is not complected with time it is treated immutably. Said differently, Functional Programming separates the what, the how, and the when.

Higher Order Functions

The first characteristic involved with Functional Programming is higher order functions. “Higher Order Function” sounds like a scary term but if you’ve written any JavaScript you’ve probably used a higher order function without realizing it.

Higher order functions are functions that can be treated like variables. A function can be assigned as a variable. A function can be sent in as an argument to another function. A function can be returned from another function.

If you’ve used a callback, ever, you’ve used a higher order function.

Yes, really. That’s all that term means.

Edit: Slight correction. A higher order function is any function that either accepts one or more functions as arguments or returns a function. Thanks /u/theonlycosmonaut.

Pure Functions

Pure functions are in my opinion one the most underrated concepts in programming today and like higher order functions they are essential to FP. A pure function (sometimes known as a stateless function) is a function that has zero side-effects.

What does that really mean? A pure function is a function that has the following characteristics.

  1. Pure functions do not mutate variables outside of their inner scope
  2. Pure functions do not mutate their arguments
  3. Pure functions do not perform any effects that influence the outside world. No HTTP requests. No DOM manipulation. No invoking impure functions.
  4. A pure function accepts zero or more arguments and returns a single value.

You can think of a pure function as one of the buttons on your handheld calculator. Think of the function behind factorial. No matter what time of day it is, no matter what you typed into the calculator ahead of time, every time you give the factorial function the same input it will always produce the same output without producing any other side-effects. Your calculator’s factorial function is a pure function.

So what’s so great about pure functions?

Pure functions are definitionally simpler. Invoking a pure function is simpler than invoking an impure function because pure functions are 100% decoupled with time. You can call a pure function with the same inputs any time of the day and always get the same output without any other side-effects. The same can’t be said of impure functions.

Pure functions are simpler to test. Think about everything you have to do to unit test much of your front-end code today. It probably includes one or more of the following.

  • Mocking Promises
  • Bootstrapping DOM
  • Bootstrapping InsertFrameworkHere.JS
  • Stubbing other impure components or functions that get called

Many of those are necessary when testing impure functions because they have side-effects that you don’t want to execute during testing. A function free of side-effects has none of that. In order to test a pure function you only need to invoke the function and make an assertion on the output. Not only does this greatly simplify testing but in my experience I’ve noticed this also happens to lower the barrier to doing TDD.

Pure functions can be memoized. Function memoization is just a fancy term for caching inputs and outputs.

Let’s say you write a pure function that is computationally expensive and this function is called over and over again. Some languages and libraries may provide a mechanism to memoize that function such that if that function is called again with the same input, it will return the same output that was cached from last time. The obvious advantage being that the expensive computation is not performed again.

Pure functions can invoke other pure functions. By having pure functions build off of one another you can build much of your application in a pure way and push many of the side-effects to the edges of your application. Elm and CycleJS work like this. Elm, for example, is a language where all of the Elm code that you write will be written inside of a pure function. This is actually really great because all state mutation, HTTP requests, and DOM manipulations are deferred to the Elm runtime.

There are also more well-known examples like Redux’s reducer function, React’s render function, and reselect selectors. All three of these are examples of pure functions.

Immutable Data

Immutable data is exactly what it sounds like. It’s data that cannot be mutated. If you do want to change some data you would use some other strategy to create a mutated copy of that data.

There are a handful of strategies to accomplish this using JavaScript, some of which are:

Isn’t it inefficient to create new references?

Yes, unless your solution uses structural sharing or unless the size and frequency at which you mutate your data is negligible.

Sometimes partial structural sharing is sufficient. For example, I’m using Redux at work and on every Redux action dispatched the vast majority (more than 80%) of our application state doesn’t change at all and the same reference is reused.

A look at curry and compose

There are two functions in particular associated with Functional Programming that are helpful to know because they are incredibly powerful. Those functions are curry and compose.

The best JavaScript library that I’ve come across for applying these functional concepts is Ramda so I’ll be using Ramda in the examples to follow.

Function currying

A curried function is a function that can be invoked with fewer arguments than it was expecting. When a curried function is partially applied a new curried function is returned that expects the remaining arguments. When all arguments are supplied, the function is invoked.

Neat… but when am I ever going to use this?

Function currying is useful for pre-populating one or more function arguments with different values.

Here’s a short example that curries a helper function used to get a value in a production or development configuration record:

Function composition

Function composition is useful if you need to perform a series of data transformations. For example, let’s say I have a couple of person records that have a first and last name.

const people = [
{ name: 'Charlie Koster' },
{ name: 'John Doe' }
];

Now let’s assume I’m building a feature that requires that I display a list of people but with specially formatted names. Rather than displaying “John Doe” we want to display “DOE, J.”

There are a lot of ways to accomplish that, but I’m going to write a few different functions and compose them together. I need the following functions:

  1. A function to capitalize the name of a person;
  2. A function to split the name into an array and reverse it;
  3. A function to abbreviate the first name and add a comma after the last name.

In keeping with FP principles let’s write these as pure functions.

// capitalizeNames : Person -> String
const capitalizeNames = (person) => person.name.toUpperCase();
// splitAndReverseNames : String -> Array<String>
const splitAndReverseNames = (name) => R.reverse(name.split(' '));
// formatNames : Array<String> -> String
const formatNames = (names) => {
const abbreviatedFirst = names[1].substr(0, 1);
return names[0] + ', ' + abbreviatedFirst + '.';
};

Notice how the return type of capitalizeNames matches the argument type of splitAndReverseNames, and how the return type of splitAndReverseNames matches the argument type of formatNames.

What this allows us to do is use compose to create a pipeline of functions where the output of one function is sent as input to the next. These functions can be composed together to create a new function that accepts a Person record as input and returns a formatted name.

A quick note, Ramda’s compose reads right to left so the order that the helper functions are called is capitalizeNames, then splitAndReverseNames, then formatNames.

Analysis

Let’s highlight some points in that last example.

The capitalizeNames, splitAndReverseNames, and formatNames functions are pure functions. The fact that those functions are pure indicates that those functions will never mutate their arguments, they will never mutate any other variable in the application, they will never produce any side-effects, and they are straightforward to unit test.

The “people” array is treated as if it was immutable. After running the contents of people through those pure functions that array is unchanged and we can continue to use it for entirely different purposes. In fact, there is not a single line of code in that example where a variable is mutated.

We are transforming data instead of asking anthropomorphized objects to perform behavior. Functional Programming is really about separating data from behavior. If I had instead written the above example in an OOP way you might expect something like this:

While this example is slightly unfair and exaggerated, it demonstrates just how quickly data, behavior, and time can become complected. For example, on line 18 person1 is an object with the name it was initialized with, but on line 23 that person’s name has been mutated. Should it be the caller’s job to know that formatPeopleNames mutates its argument? Can we call formatPeopleNames at a later time and expect the same results? Are data, behavior, and time decoupled in the above example? No, no, and no.

Wrap up

Functional Programming can really improve the maintainability of an application. Although it reads a little less naturally to some, the decoupling of data, behavior, and time can do wonders in simplifying applications. This simplification can work on a small scale, like with ReactJS and Redux, or on an application-wide scale, like with Elm and CycleJS and others.

I hope this helped clarify what Functional Programming is — please reach out if you have questions.

--

--

Statuscode

Principal Software Architect | Conference Speaker | Blog Author