6 Reasons Why You Should Write Functional Code

I've talked a lot about functional programming, and a bit about why you ought to do it. Here's where we get into detail about why I think programming functionally is a good idea.

Pure functions can be thought about in isolation

Here's a really simple (and pure) function:

var add = (a, b) => a + b  

Look at all the things I don't have to think about:

  • I don't have to worry if outside state is going to change the result of calling this function.
  • I don't have to worry if calling this function will change some other part of program state.

This function just has two inputs, and one output. Anywhere I call it, if I put in the same two inputs I will always get the same result.

All the mental overhead of having to keep track of overall application state is just gone...

Functional code is easier to test

Here's a simple test for an add function:

describe('add', () => {  
    var add = require('../add.js')

    it(
        'should return the sum of its two arguments',
        function () {
            expect(add(1, 1)).toEqual(2)
        })
})

I know that this is a simple example, but look at what you don't have to do to test this code. Because add doesn't depend on outside state and doesn't affect outside state, testing just got super easy.

If most of your code is purely functional, all you have to worry about when testing it are making sure the inputs and the output are correct, nothing else.

Functional code is easier to debug

Here's an example of some code with a bug:

var comma_separate = (width) =>  
    R.compose(
        R.join(','),
        R.reverse,
        R.splitEvery(width),
        R.reverse)

var format_dollars =  
    R.compose(
        R.concat('$'),
        comma_separate(3),
        R.toString)

format_dollars(100000)  
//returns '$001,000', what gives?

Because this code is a composition of functions, a pipe through which data flows and is transformed, all we have to do to see what went wrong is to look data in various stages of transformation.

A function like tap can help us do that:

var tap = function (x) {  
    console.log(x)
    return x
}

Now, I realize that this code isn't pure, it has the side effect of printing to the screen.

meh.

Anyway, here's how I'd use tap with our problem code:

var comma_separate = (width) =>  
    R.compose(
        R.join(','),
        tap,
        R.reverse,
        R.splitEvery(width),
        R.reverse)

//prints [ '001', '000' ]

Oh hey! It turns out I need to R.map(R.reverse) this array. This should take care of it:

var comma_separate = (width) =>  
    R.compose(
        R.join(','),
        R.map(R.reverse), //Here's our fix
        R.reverse,
        R.splitEvery(width),
        R.reverse)

Functional code is easier to refactor

Since functional code tends to look like a chain of functions all lined up in a row, it becomes pretty easy to add new behavior by adding to the beginning, end, or somewhere in the middle of that chain.

Here's an example of extending a format_dollars function to format_dollars_cents function:

var format_dollars_cents =  
    R.compose(
        R.join('.'),
        (money) => [
            format_dollars(money[0]),
            money[1] ? money[1].slice(0, 2) : '00'],
        R.split('.'),
        R.toString)

format_dollars_cents(100000)  
//returns $100,000.00

format_dollars_cents(100000.234)  
//returns $100,000.23

format_dollars_cents(.234)  
//returns $0.23

Here we're simply piping in the functionality of format_dollars by calling it on part of our data in format_dollars_cents.

Building functions from other functions. Hooking them all together like water pipes in your house. That's what it's all about. :)

Functional code is at a higher level of abstraction

Functional code tends to be code that tells the computer what to do rather than how to do it. The details are abstracted away.

Here's another look at that format_dollars function:

var comma_separate = (width) =>  
    R.compose(
        R.join(','),
        R.map(R.reverse),
        R.reverse,
        R.splitEvery(width),
        R.reverse)

var format_dollars =  
    R.compose(
        R.concat('$'),
        comma_separate(3),
        R.toString)

format_dollars(100000)  
//returns $100,000

Nowhere in this code have I declared a variable, or even listed arguments. This style of coding is called point free. You can't get away with writing your whole app this way, but you can do this a lot.

I look at functions like format_dollars as a way for me to talk to the computer the way I'd prefer to do it:

"First turn the input into a string. Next, comma separate that string in groups of 3, then put a dollar sign at the start of the string."

The computer (or rather, the library writers) can worry about how all that gets done. I don't have to be concerned about it.

Functional programming is fun

I don't think the importance of fun can be easily over-stated.

When a language or environment is fun, it further motivates you to learn. It motivates you to make cool stuff.

I personally really enjoy the challenge of programming in this way. I'm still on the journey and am having a blast!

Looking for a software developer?