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

Posted on November 1, 2016

This is part two 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!

Functional Curry 2: Electric Vindaloo

In the first post I showed a simple, common example of a pattern of code you’ll find in almost any code base. Anywhere you find a function that calls another function to kick off a side-effect is a good place to “lift” that side-effect into a parameter of a curried function. It’s an easy pattern to spot and a quick win using functional programming techniques to make your code more beautiful, flexible, and easier to maintain.

But what else is currying good for?

You might have heard functional programmers mention composition at some point. Currying is a fundamental way of composing functions together. Composition is the process of building up interesting functions from small, simple ones… without having to re-write or modify source code.

Remember the open-close principle? Let’s take it up a step.

One common pattern you will find in Javascript code is this:

const bobsBurgers = [
  {name: 'Bob', email: 'bob@burgers.com', age: 42},
  {name: 'Linda', email: 'linda@burgers.com', age: 42},
  {name: 'Louise', email: 'louise@burgers.com', age: 9},
  {name: 'Gene', email: 'gene@burgers.com', age: 11},
  {name: 'Tina', email: 'zombielover@burgers.com', age: 14}
]

const sortEmployees = employees => {
    return employees.sort((e1, e2) => {
      switch(true) {
        case (e1.age < e2.age):
          return 1
        case (e1.age === e2.age):
          return 0
        case (e1.age > e2.age):
          return -1
      }
    })
}

console.log(sortEmployees(bobsburgers))

This is a fairly trivial example but you will see that the dot-notation is not composable. What if I told you that we could write an ordinary comparison function, compose it with a function for accessing the properties of our employee objects and compose them together into a function for sorting employees?

We’ll be using a utility library called Ramda, which I mentioned in the first post, to help us out. The reason for using Ramda is to help us avoid those caveats I mentioned at the end of the first post. Ramda is a library for combining and gluing functions together.

I can sense your anticipation. So without further adieu:

const R = require('ramda')

const compare = a => b => a > b ? 1 : a === b ? 0 : -1

const upon = f => g => (a, b) => {
    return f(g(a))(g(b))
} // our first combinator!

const byAge = upon(compare)(R.prop('age'))

console.log(R.sort(byAge, bobsBurgers))

I like this example for two reasons. First: it’s concise and straight to the point. It also demonstrates a subtle point about composition that you might miss in a larger example.

Let’s look at our combinator, upon. Anyone who is new to functional programming and curious has probably come across code with single-variable names sprinkled around some violent usage of operators. It looks horrible and is a huge turn off. There’s a reason for it however and understanding it is unavoidable to using more advanced functional programming techniques.

The reason is that there are no good names for f and g or the subsequent parameters of the final, anonymous function. That’s because combinators are glue between functions. We like nice, descriptive names and I encourage you to use them in the public interface to your library. However combinators are special tools and it’s best to focus instead on the relationship of the symbols (ie: the operators) and the position of the arguments.

In the case of upon it takes a binary function, f (binary meaning a function of two arguments), and a unary function g (unary meaning a function of one argument). It returns a function that takes two parameters, a, b and supplies them to the parameters of f by applying g to each. Combinators make other functions by combining smaller functions together; usually by transforming parameters between them.

The cool thing here is that we didn’t have to write out the definition of an anonymous function that knows about the structure of employee objects. Instead we composed two smaller, generic functions together to make a new function, byAge.

This brings me nicely into the second point. By composing functions together in this manner and giving them more descriptive names as we move up in levels of abstraction we actually end up with code that encodes the domain of the problem into the source code.

In our original example you have to read the entire function, switch-statement and all, in order to understand how we’re sorting the employee array. In our improved functional programming example we get a description of what the code is doing by reading it:

R.sort(byAge, bobsBurgers)

Now the same could be said about the first example. It could be re-written to name the anonymous function and achieve the same effect. Almost.

Remember how I said the dot-notation is not composable? Let’s say our data is a little more complicated:

const bobsBurgers = [
  {id: 1, employee: {
      name: 'Bob',
      email: 'bob@burgers.com',
      age: 44
  }},
  {id: 2, employee: {
      name: 'Linda',
      email: 'linda@burgers.com',
      age: 44
  }},
  {id: 3, employee: {
      name: 'Louise',
      email: 'louise@burgers.com',
      age: 9
  }},
  {id: 4, employee: {
      name: 'Tina',
      email: 'zombielover@burgers.com',
      age: 13
  }},
  {id: 5, employee: {
      name: 'Gene',
      email: 'gene@burgers.com',
      age: 11
  }}
]

So let’s return a list of 'Tina: zombielover@burgers.com' strings sorted by age as before. Let’s use function composition to do it:

const getEmployee = R.prop('employee')
const byEmployeeAge = upon(compare)(R.path(['employee', 'age']))
const nameAndEmail = R.compose(R.join(': '), R.props(['name', 'email']))
const employeesByAge = R.sort(byEmployeeAge)

console.log(R.map(row => nameAndEmail(getEmployee(row)),
                  employeesByAge(bobsBurgers)))

Look ma! No dot-notation. No loops or intermediate values. Only declarations. There’s quite a bit of plain-old Javascript under the hood here hidden by Ramda… but the power of composing functions together by currying allows us to write descriptive, beautiful code that is declarative and easy to maintain.

Conclusion

This concludes the dive into function currying. I hope you found it delicious. In the next part of my talk I glossed over Monads but I gave a hint as to how we can use them in our work-a-day code to make it better, more beautiful, and easier to understand! You don’t have to understand Monads in order ot use them… many Javascript programmers already do without even realizing it!

Next time we’ll dive into Monads with just enough detail to show you the patterns in your existing code you can use to identify where Monads will help you write clear, robust, beautiful code.