A (mostly) Practical Guide to Functional Programming (in Javascript) Part 3

Posted on May 13, 2017

This is part three of a series of posts where I show you some patterns and tricks for improving every day code with functional programming. If you haven’t seen the first post go read it first. You might not understand everything in this post if you’re just starting out in the world of functional programming!

The Humble Burrito

Why does the myth that Monads are hard continue to persist? They’re a useful tool that can clean up any code base and make hard problems seem easy. The essential problem monads address in computer programs is composition. Do you see a theme here? Monads in particular are great for structuring nested computations.

If you’ve ever written a piece of code that takes some input or state and then proceeds through a chain of transformations you could’ve benefitted from monads. The reason is that they compose well together and let you control where, and sometimes when, those steps happen… from the outside.

It sounds a little abstract so let’s jump into our example:

const users = [
    {
        id: 1,
        profile: {
            first: "John",
            last: "Doe"
        }
    },
    {
        id: 2,
        profile: {
            first: "Jane",
            last: "Doe"
        }
    },
    {
        id: 3
    },
    {
        id: 4,
        profile: {
            fst: "Foo",
            last: "Bar"
        }
    },
    {
        id: 5,
        profis: {
            first: "Derp",
            last: "Dop"
        }
    }
]

Here we’re defining a data strucure to work with to illustrate a simple example where we can use functional programming techniques to make our code more beautiful, maintainable, and resistant to errors.

The problem we sometimes face as programmers is unreliable input. We would like it if our data was always formatted precisely but sometimes scripts encounter strange behaviours when processing large amounts of data or sometimes it’s plain human error when entering it. Either way we’d like our code to not break if we encounter a bad input. It would also be nice if we could re-use that code in case the specifications change in the future.

So let’s write a function to get the full name of a user object from this list. We’ll write it in a straight-forward style:

const getFullName = user => {
    const first = user.profile.first
    ,     last = user.profile.last;
    return `${first} ${last}`
}

You may have code that looks like this in your code base or have come across it before somewhere else. Of course this is pretty naive since our third user in the list doesn’t even have a profile.

const getFullName = user => {
    const profile = user.profile
    ,     first = profile ? profile.first : undefined
    ,     last = profile ? profile.last : undefined;
    return first && last
        ? `${first} ${last}`
        : `unknown`
}

Now we’re getting a little more defensive. Once you’ve been programming in Javascript for a while you learn to recognize where accessing an attribute from an object that could be undefined can bite you. This version isn’t too bad.

And if we want to get the whole list of names

users.map(getFullName)

Right on! Problem solved!

Enter the Monad

Not so fast. This is just a trivial example. You’ve seen much larger functions where you’ve had to use these defensive techniques to avoid failing your unit tests. You’ve let a good chunk of your code base be taken up by testing for undefined and null. There are if statements everywhere. Is there a better way?

I’m glad you asked. First some preliminaries. I would like to direct you to Professor Frisby’s course on egghead.io. However if you don’t have the time to go through that and jump back here I’ll give you the cliff’s notes.

Did you see map before? Isn’t it wonderful? We love chaining things together like that in Javascript. We can chain map together like that all day long to transform some values.

[1, 2, 3]
.map(x => x + 1)
.map(x => x * x)

In fact we don’t really give it a whole list if we just want to transform a single value. The important thing is that we put those values in a box. When we do that we get the map method on that box.

Monads are like boxes. They are a data type with a value inside and some methods for interacting with that value. That’s it.

To illustrate our point let’s create a monad right now:

const Box = x => ({
    x,
    map: f => Box(f(x))
})

This is enough to let us do our map thing again:

Box(4)
.map(x => x * x)
.map(x => x + 1)

We’ll need a few more methods in our interface to make a true monad type. However the point of me showing you box is so that you understand the concept and begin to realize that the dense terminology is just terminology. You can understand the concepts. The terminology will come with time.

So the full list of methods our Box would have to implement is thankfully enumerated in the Fantasy Land Spec.

For our purposes in the rest of this article we’ll rely upon an implementation of a monad called, option. This type represents a value that may not exist. There are two possible instances you can create from the option type: Some or None. Either you have some value or you have none. To play off of box let’s look at how you could implement your own option monad:

const Some = x => ({
    x,
    map: f => Some(f(x)),
    chain: f => f(x), // this is new
    inspect: () => `Some(${x})` // so we can see our value
})

const None = () => ({
    map: f => None(),
    chain: f => None(),
    inspect: () => `None()`
})

We added a few things here. We added the chain method. What does that do? Well we saw how map applies a function to the value in our box. What if that value in this case is another option type?

const maybeUserProfile = user =>
    'profile' in user
    ? Some(user.profile)
    : None()

const maybeProfileEmail = profile =>
    'email' in profile
    ? Some(profile.email)
    : None()

// and we can chain these together like so...
const maybeUserEmail = user =>
    maybeUserProfile(user)
    .chain(maybeProfileEmail)

maybeUserEmail({profile: {email: 'foo@bar'}}).inspect() // Some('foo@bar')
maybeUserEmail({profile: {foo: 'bar'}}).inspect() // None()

We use .chain when our function f returns another box. This lets us pass values along… in a chain. Get it? Not all of the terminology is dense.

Now let’s talk about None for a second. It’s the same type as Some… it’s an instance of option. It represents the absence of a value. When we have multiple instances of a single type we call it a, sum type. option is a sum type.

What’s really cool about monads is how they compose together. We can map over a Some and some stage of our computation might return a None and that’s okay! We don’t have to explicitly check for it; it’s all passed through.

Let’s look at how this plays out with our library which implements a complete option type:

const option = require('fantasy-maybes')
const R = require('ramda')

const maybeUserProfile = user =>
    'profile' in user
    ? option.Some(user.profile)
    : option.None

const profile = maybeUserProfile({id: 1,
                                  profile: {first: 'Foo',
                                            last: 'Bar'})

const fullName = profile
    .map(p => `${p.first} ${p.last}`)
    .getOrElse(`Unknown`)

Here we have something that is starting to look like how you’d write our original example using monads. It’s still not complete. We still need to handle the case where the profile object isn’t what we expect. However this is a good start.

First notice our .getOrElse method. We’re treating monads like boxes for now. This method is the way our library lets us get a value out of the box once we’re done. Notice anything else that seems interesting about this code?

What does maybeUserProfile return? It’s not a profile!

It returns an instance of our option type. It can return either a Some instance or a None instance. These instances have the same methods that make them monads so we can treat them just the same in our code. We can call .map and friends freely without worrying if the object passed into maybeUserProfile had the right property.

It also means that we can build up our computation dynamically and only evaluate the result when we want to. Pay attention to this when you’re exploring monads: it’s one of their most salient benefits.

One last observation: the caller of maybeUserProfile gets to determine the final value in the case where None is returned. You might hear the term, inversion of control when people talk about monads. This is one way that happens. We’ll see more about this as we expand on our example.

Maybe Ramda

Ramda is a library we’ve used a number of times in this series. It’s a great library filled with helpful functions for combining and creating new, interesting functions. We like composing small things to make bigger ones. Ramda is great.

However when working with our new-found monad powers we may find Ramda lacking. There are many functions that return undefined and often we’d rather get back an instance of None.

Fortunately Ramda is easy to extend with our new-found powers. We’re going to quickly create a module to help us use option types with Ramda.

// some-ramda.js

const option = require('fantasy-maybes')
const R = require('ramda')

const maybeValue = v => v !== null && typeof v !== 'undefined'
    ? option.Some(v)
    : option.None

const maybeIndex = v => v !== -1
    ? option.Some(v)
    : option.None

const maybeF = f => R.compose(maybeValue, f)
const maybeI = f => R.compose(maybeIndex, f)

module.exports = {
    find: maybeF(R.find),
    findIndex: maybeI(R.findIndex),
    findLast: MaybeF(R.findLast),
    findLastIndex: MaybeI(R.findLastIndex),
    prop: maybeF(R.prop),
    ...
}

There are two kinds of functions we want to wrap in Ramda: functions that may return a value and functions that may return an index. We create functions to return an instance of option for those cases and then create a helper combinator to take any function, f, and wrap its return value with our option constructors.

We then enumerate through the list of ramda functions that get a value or index and wrap them with our helpers. Now we have monads!

Some Users

Remember our list of users?

const users = [
    {
        id: 1,
        profile: {
            first: "John",
            last: "Doe"
        }
    },
    {
        id: 2,
        profile: {
            first: "Jane",
            last: "Doe"
        }
    },
    {
        id: 3
    },
    {
        id: 4,
        profile: {
            fst: "Foo",
            last: "Bar"
        }
    },
    {
        id: 5,
        profis: {
            first: "Derp",
            last: "Dop"
        }
    }
]

Let’s use our new powers to re-write our original example. First let’s write a function that takes a user object and returns Some profile:

const M = require('some-ramda')

const userProfileM = user => M.prop('profile', user)

That’s good. We’re getting used to monads now so we’re going to abbreviate our maybeSomething pattern to somethingM instead. We can get a profile from a user object and if there isn’t one we’ll just get None. Now we can write a function to get the full name of the user from their profile without worrying about whether the profile is there or not.

const M = require('some-ramda)

const userFullNameM = user =>
    userProfileM(user)
    .chain(p => M.prop('first', p)
        .chain(first => M.prop('last', p)
            .map(last => `${first} ${last}`)))

Whoa! We haven’t used chain that much yet… this looks like a lot to take in. Don’t despair! You know what chain does, remember? We use map to apply a function to the value in our box. We use chain if our function returns another box. Since prop returns an option type we need to use chain.

This looks a little bit harder to follow than our original implementation of getFullName.

const getFullName = user => {
    const profile = user.profile
    ,     first = profile ? profile.first : undefined
    ,     last = profile ? profile.last : undefined;
    return first && last
        ? `${first} ${last}`
        : `unknown`
}

What has all of these nested .chain calls given us?

Remember when we talked about inversion of control? What can we observe about the value of getFullName?

It’s a string. A string that we, the caller, cannot change. If we want to know that the lookup failed we’ll have to parse this string… and that’s not a very good interface. Our monads let our callers decide how to handle the None instance.

You might say that we could return a sentinel value from getFullName. How well does that compose? Or maybe a callback?

Our userFullNameM function returns an option instance which means we can compose it together with other options (and with a little help, even other monads!). And we get to build our computation dynamically and only evaluate it when we choose. If we chose to return a sentinel value… well we’d be inventing our own protocols and abstractions. Our users might know monads and monads have laws so let’s stick with them.

Let’s try searching for a user in our list:

const M = require('some-ramda')

const findUserM = (users, id) =>
    M.find(R.propEq('id', id), users)

const findUserNameById = (users, id) =>
    findUserM(users, id)
    .chain(userFullNameM)

There’s a lot more we could do. This is great!

Conclusion

This article was a little longer than the others. That’s okay. We had to cover a little more ground. However we discovered a new power: monads.

So what patterns can we look for in our code, today, that we can replace with this technique to make our code more beautiful, maintainable, and bug-resistant?

Look for code that needs to transform or extract data from nested structures. That’s a great use-case for our option type. As we’ve seen it lets us eliminate extraneous conditionals from our code and forget about constantly checking for undefined and null. Start at the bottom and slowly work your way back to the top. Write some helper libraries along the way to wrap functions into option types.

But don’t stop there! There are other monads besides option which you can use to structure your code and make it better. If you’re already using Promise then you’ve actually been using a kind of monad! Keep going and learn about some other ones like either, future, state, etc. They’re great!

Happy hacking.