JavaScript
Article

An Introduction to Reasonably Pure Functional Programming

By Mark Brown

This article was peer reviewed by Panayiotis «pvgr» Velisarakos, Jezen Thomas and Florian Rappl. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

When learning to program you’re first introduced to procedural programming; this is where you control a machine by feeding it a sequential list of commands. After you have an understanding of a few language fundamentals like variables, assignment, functions and objects you can cobble together a program that achieves what you set out for it do – and you feel like an absolute wizard.

The process of becoming a better programmer is all about gaining a greater ability to control the programs you write and finding the simplest solution that’s both correct and the most readable. As you become a better programmer you’ll write smaller functions, achieve better re-use of your code, write tests for your code and you’ll gain confidence that the programs you write will continue to do as you intend. No one enjoys finding and fixing bugs in code, so becoming a better programmer is also about avoiding certain things that are error-prone. Learning what to avoid comes through experience or heeding the advice of those more experienced, like Douglas Crockford famously explains in JavaScript: The Good Parts.

Functional programming gives us ways to lower the complexity of our programs by reducing them into their simplest forms: functions that behave like pure mathematical functions. Learning the principles of functional programming is a great addition to your skill set and will help you write simpler programs with fewer bugs.

The key concepts of functional programming are pure functions, immutable values, composition and taming side-effects.

Pure Functions

A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect.

// pure
function add(a, b) {
  return a + b;
}

This function is pure. It doesn’t depend on or change any state outside of the function and it will always return the same output value for the same input.

// impure
var minimum = 21;
var checkAge = function(age) {
  return age >= minimum; // if minimum is changed we're cactus
};

This function is impure as it relies on external mutable state outside of the function.

If we move this variable inside of the function it becomes pure and we can be certain that our function will correctly check our age every time.

// pure
var checkAge = function(age) {
  var minimum = 21;
  return age >= minimum;
};

Pure functions have no side-effects. Here are a few important ones to keep in mind:

  • Accessing system state outside of the function
  • Mutating objects passed as arguments
  • Making a HTTP call
  • Obtaining user input
  • Querying the DOM

Controlled Mutation

You need to be aware of Mutator methods on Arrays and Objects which change the underling objects, an example of this is the difference between Array’s splice and slice methods.

// impure, splice mutates the array
var firstThree = function(arr) {
  return arr.splice(0,3); // arr may never be the same again
};

// pure, slice returns a new array
var firstThree = function(arr) {
  return arr.slice(0,3);
};

If we avoid mutating methods on objects passed to our functions our program becomes easier to reason about, we can reasonably expect our functions not to be switching things out from under us.

let items = ['a','b','c'];
let newItems = pure(items);
// I expect items to be ['a','b','c']

Benefits of Pure Functions

Pure functions have a few benefits over their impure counterparts:

  • More easily testable as their sole responsibility is to map input -> output
  • Results are cacheable as the same input always yields the same output
  • Self documenting as the function’s dependencies are explicit
  • Easier to work with as you don’t need to worry about side-effects

Because the results of pure functions are cacheable we can memoize them so expensive operations are only performed the first time the functions are called. For example, memoizing the results of searching a large index would yield big performance improvements on re-runs.

Unreasonably Pure Functional Programming

Reducing our programs down to pure functions can drastically reduce the complexity of our programs. However, our functional programs can also end up requiring Rain Man’s assistance to comprehend if we push functional abstraction too far.

import _ from 'ramda';
import $ from 'jquery';

var Impure = {
  getJSON: _.curry(function(callback, url) {
    $.getJSON(url, callback);
  }),

  setHtml: _.curry(function(sel, html) {
    $(sel).html(html);
  })
};

var img = function (url) {
  return $('<img />', { src: url });
};

var url = function (t) {
  return 'http://api.flickr.com/services/feeds/photos_public.gne?tags=' +
    t + '&format=json&jsoncallback=?';
};

var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
var mediaToImg = _.compose(img, mediaUrl);
var images = _.compose(_.map(mediaToImg), _.prop('items'));
var renderImages = _.compose(Impure.setHtml("body"), images);
var app = _.compose(Impure.getJSON(renderImages), url);
app("cats");

Take a minute to digest the code above.

Unless you have a background in functional programming these abstractions (curry, excessive use of compose and prop) are really difficult to follow, as is the flow of execution. The code below is easier to understand and to modify, it also much more clearly describes the program than the purely functional approach above and it’s less code.

  • The app function takes a string of tags
  • fetches JSON from Flickr
  • pulls the URLs out the response
  • builds an array of <img> nodes
  • inserts them into the document
var app = (tags)=> {
  let url = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${tags}&format=json&jsoncallback=?`
  $.getJSON(url, (data)=> {
    let urls = data.items.map((item)=> item.media.m)
    let images = urls.map((url)=> $('<img />', { src: url }) )

    $(document.body).html(images)
  })
}
app("cats")

Or, this alternative API using abstractions like fetch and Promise helps us clarify the meaning of our asynchronous actions even further.

let flickr = (tags)=> {
  let url = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${tags}&format=json&jsoncallback=?`
  return fetch(url)
  .then((resp)=> resp.json())
  .then((data)=> {
    let urls = data.items.map((item)=> item.media.m )
    let images = urls.map((url)=> $('<img />', { src: url }) )

    return images
  })
}
flickr("cats").then((images)=> {
  $(document.body).html(images)
})

Note: fetch and Promise are upcoming standards so they require polyfills to use today.

The Ajax request and the DOM operations are never going to be pure but we could make a pure function out of the rest, mapping the response JSON to an array of images – let’s excuse the dependence on jQuery for now.

let responseToImages = (resp)=> {
  let urls = resp.items.map((item)=> item.media.m )
  let images = urls.map((url)=> $('<img />', { src: url }))

  return images
}

Our function is just doing two things now:

  • mapping response data -> urls
  • mapping urls -> images

The “functional” way to do this is to create separate functions for those two tasks and we can use compose to pass the response of one function into the other.

let urls = (data)=> {
  return data.items.map((item)=> item.media.m)
}
let images = (urls)=> {
  return urls.map((url)=> $('<img />', { src: url }))
}
let responseToImages = _.compose(images, urls)

compose returns a function that is the composition of a list of functions, each consuming the return value of the function that follows.

Here’s what compose is doing, passing the response of urls into our images function.

let responseToImages = (data)=> {
  return images(urls(data))
}

It helps to read the arguments to compose from right to left to understand the direction of data flow.

By reducing our program down to pure functions it gives us a greater ability to reuse them in the future, they are much simpler to test and they are self documenting. The downside is that when used excessively (like in the first example) these functional abstractions can make things more complex which is certainly not what we want. The most important question to ask when refactoring code though is this:

Is the code easier to read and understand?

Essential Functions

Now, I’m not trying to attack functional programming at all. Every developer should make a concerted effort to learn the fundamental functions that let you abstract common patterns in programming into much more concise declarative code, or as Marijn Haverbeke puts it..

A programmer armed with a repertoire of fundamental functions and, more importantly, the knowledge on how to use them, is much more effective than one who starts from scratch. – Eloquent JavaScript, Marijn Haverbeke

Here is a list of essential functions that every JavaScript developer should learn and master. It’s also a great way to brush up on your JavaScript skills to write each of these functions from scratch.

Arrays

Functions

Less Is More

Let’s look at some practical steps we can take to improve the code below using functional programming concepts.

let items = ['a', 'b', 'c'];
let upperCaseItems = ()=> {
  let arr = [];
  for (let i = 0, ii = items.length; i < ii; i++) {
    let item = items[i];
    arr.push(item.toUpperCase());
  }
  items = arr;
}

Reduce functions dependence on shared state

This may sounds obvious and trivial but I still write functions that access and modify a lot of state outside of themselves, this makes them harder to test and more prone to error.

// pure
let upperCaseItems = (items)=> {
  let arr = [];
  for (let i = 0, ii = items.length; i < ii; i++) {
    let item = items[0];
    arr.push(item.toUpperCase());
  }
  return arr;
}

Use more readable language abstractions like forEach to iterate

let upperCaseItems = (items)=> {
  let arr = [];
  items.forEach((item) => {
    arr.push(item.toUpperCase());
  });
  return arr;
}

Use higher level abstractions like map to reduce the amount of code

let upperCaseItems = (items)=> {
  return items.map((item)=> item.toUpperCase())
}

Reduce functions to their simplest forms

let upperCase = (item)=> item.toUpperCase()
let upperCaseItems = (items)=> items.map(upperCase)

Delete code until it stops working

We don’t need a function at all for such a simple task, the language provides us with sufficient abstractions to write it out verbatim.

let items = ['a', 'b', 'c']
let upperCaseItems = items.map((item)=> item.toUpperCase())

Testing

Being able to simply test our programs is a key benefit of pure functions, so in this section we’ll set up a test harness for our Flickr module we were looking at earlier.

Fire up a terminal and have your text editor poised and ready, we’ll use Mocha as our test runner and Babel for compiling our ES6 code.

mkdir test-harness
cd test-harness
npm init -yes
npm install mocha babel-register babel-preset-es2015 --save-dev
echo '{ "presets": ["es2015"] }' > .babelrc
mkdir test
touch test/example.js

Mocha has a bunch of handy functions like describe and it for breaking up our tests and hooks such as before and after for setup and teardown tasks. assert is a core node package that can perform simple equality tests, assert and assert.deepEqual are the most useful functions to be aware of.

Let’s write our first test in test/example.js

import assert from 'assert';

describe('Math', ()=> {
  describe('.floor', ()=> {
    it('rounds down to the nearest whole number', ()=> {
      let value = Math.floor(4.24)
      assert(value === 4)
    })
  })
})

Open up package.json and amend the "test" script to the following

mocha --compilers js:babel-register --recursive

Then you should be able run npm test from the command line to confirm everything is working as expected.

Math
  .floor
    ✓ rounds down to the nearest whole number

1 passing (32ms)

Boom.

Note: You can also add a -w flag at the end of this command if you want mocha to watch for changes and run the tests automatically, they will run considerably faster on re-runs.

mocha --compilers js:babel-register --recursive -w

Testing Our Flickr Module

Let’s add our module into lib/flickr.js

import $ from 'jquery';
import { compose } from 'underscore';

let urls = (data)=> {
  return data.items.map((item)=> item.media.m)
}
let images = (urls)=> {
  return urls.map((url)=> $('<img />', { src: url })[0] )
}
let responseToImages = compose(images, urls)

let flickr = (tags)=> {
  let url = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${tags}&format=json&jsoncallback=?`
  return fetch(url)
  .then((response)=> response.json())
  .then(responseToImages)
}

export default {
  _responseToImages: responseToImages,
  flickr: flickr,
}

Our module is exposing two methods: flickr to be publicly consumed and a private function _responseToImages so that we can test that in isolation.

We have a couple of new dependencies: jquery, underscore and polyfills for fetch and Promise. To test those we can use jsdom to polyfill the DOM objects window and document and we can use the sinon package for stubbing the fetch api.

npm install jquery underscore whatwg-fetch es6-promise jsdom sinon --save-dev
touch test/_setup.js

Open up test/_setup.js and we’ll configure jsdom with our globals that our module depends on.

global.document = require('jsdom').jsdom('<html></html>');
global.window = document.defaultView;
global.$ = require('jquery')(window);
global.fetch = require('whatwg-fetch').fetch;

Our tests can sit in test/flickr.js where we’ll make assertions about our functions output given predefined inputs. We “stub” or override the global fetch method to intercept and fake the HTTP request so that we can run our tests without hitting the Flickr API directly.

import assert from 'assert';
import Flickr from "../lib/flickr";
import sinon from "sinon";
import { Promise } from 'es6-promise';
import { Response } from 'whatwg-fetch';

let sampleResponse = {
  items: [{
    media: { m: 'lolcat.jpg' }
  },{
    media: { m: 'dancing_pug.gif' }
  }]
}

// In a real project we'd shift this test helper into a module
let jsonResponse = (obj)=> {
  let json = JSON.stringify(obj);
  var response = new Response(json, {
    status: 200,
    headers: { 'Content-type': 'application/json' }
  });
  return Promise.resolve(response);
}

describe('Flickr', ()=> {
  describe('._responseToImages', ()=> {
    it("maps response JSON to a NodeList of <img>", ()=> {
      let images = Flickr._responseToImages(sampleResponse);

      assert(images.length === 2);
      assert(images[0].nodeName === 'IMG');
      assert(images[0].src === 'lolcat.jpg');
    })
  })

  describe('.flickr', ()=> {
    // Intercept calls to fetch(url) and return a Promise
    before(()=> {
      sinon.stub(global, 'fetch', (url)=> {
        return jsonResponse(sampleResponse)
      })
    })

    // Put that thing back where it came from or so help me!
    after(()=> {
      global.fetch.restore();
    })

    it("returns a Promise that resolves with a NodeList of <img>", (done)=> {
      Flickr.flickr('cats').then((images)=> {
        assert(images.length === 2);
        assert(images[1].nodeName === 'IMG');
        assert(images[1].src === 'dancing_pug.gif');
        done();
      })
    })

  })
})

Run our tests again with npm test and you should see three assuring green ticks.

Math
  .floor
    ✓ rounds down to the nearest whole number

Flickr
  ._responseToImages
    ✓ maps response JSON to a NodeList of <img>
  .flickr
    ✓ returns a Promise that resolves with a NodeList of <img>


3 passing (67ms)

Phew! We’ve successfully tested our little module and the functions that comprise it, learning about pure functions and how to use functional composition along the way. We’ve separated the pure from the impure, it’s readable, comprised of small functions, and it’s well tested. The code is easier to read, understand, and modify than the unreasonably pure example above and that’s my only aim when refactoring code.

Pure functions, use them.

That’s all for now! Thanks for reading and I hope you have found this a good introduction to functional programming, refactoring and testing in JavaScript. It’s an interesting paradigm that’s making waves at the moment, due largely to the growing popularity of libraries like React, Redux, Elm, Cycle and ReactiveX which encourage or enforce these patterns.

Jump in, the water is warm.

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

  • Steve Rogers

    Interesting article, Mark. As a somewhat of an JS programmer that knows beyond the basics of writing JavaScript, I always wanted to know the ways of functional programming in JavaScript to help improve my coding style. I noticed your code uses ECMA script 6 features, but is it necessary to use them? Lots of users in Codewars uses them, just to decrease their lines of coding, but I don’t know whether that’s a smart way to go or not.

  • godlyChaos

    Real nice post, I love the distinction between unreasonable and reasonably pure.

    Not that it is a big deal, but under the “Less Is More” section, did you mean to put
    “let item = items[0];”
    or was that supposed to be
    “let item = items[i];”

    • markbrown4

      Thanks! Yes, good catch – that’s a typo.

  • sstur

    @markbrown4:disqus: that fetch() example won’t work. When the promise resolves, you don’t yet have the response body and it certainly won’t be parsed from JSON. You will have a Response object which gives you access to the response headers and you will probably want to call the .json() method which will return a new promise that will resolve when the body has been received and parsed.
    Edit: the first fetch example is the one I’m referring to. I see you’ve done it correctly in the second example.

    • markbrown4

      Yes, missed the .then((resp)=> resp.json()) in the first example. Cheers

      • sstur

        Wow, quick fix! Great article by the way.

  • http://www.skooppa.com s.molinari

    Nice article. You mentioned objects at the beginning. And although you can program functionally in JS, isn’t it better or rather more advantageous to go with OOP, especially when the application gets bigger?

    • markbrown4

      OOP works well for certain things, it does grinds against FP principles though as an objects methods rely on and mutate their own state via `this` so the functions are never pure. It’s a misconception that OOP is required to help you scale an application as it grows.

      Functional Programming is a different approach to OOP, though there’s no reason you can’t use a mix of the two.

      • http://www.skooppa.com s.molinari

        Agreed, however, I don’t think any knowledgeable programmer would insist OOP is required, in order to organize a big application (scaling). It’s a choice. And IMHO, the better choice, with its disadvantages too. But, the advantages outweigh the disadvantages by far.

        Let me ask this question, what is the difference between the flikr module and a class?

        Scott

        • markbrown4

          I avoid inheritance entirely and I reserve the use of classes for the times that I need multiple instances of the same type of object. Those are times when an object that holds a copy of it’s own state comes in handy.

          The flickr module is a single function that returns ‘s, a class would complicate rather than help here. There’s no right and wrong answers, if you like the semantics of classes you can use them for everything, I prefer singular functions if they do the job.

          • http://www.skooppa.com s.molinari

            I can agree with that too, except with the inheritance bit. That is a non-sequitur. You can completely avoid inheritance in OOP too. Let’s continue in that direction though.

            Let’s say your application now needs to ingest images from other sites like imgr or deviantart too. Would you make two new modules with basically the same or similar functions? Or would you export out the common functions into some other module? Or would you consider this another good reason to go OOP and actually use inheritance to better support code reuse?

            Remember, our app is growing and changing and needing new features constantly.

            And, you do agree, a main goal of practically every application is to constantly grow and improve in order to keep the users/ customers happy, right? This goal is, IMHO, easier to achieve with OOP and why it has dominated programming for almost the last 3 decades.;-)

            Like you say, there is no right or wrong. But there is a better or worse way to get a job done. For smaller applications, functional is quick and easy and gets the job done for sure. For larger applications, functional can get messy and complicated, if not done right. And the same goes for OOP too, for sure. Still, if I had a choice, OOP is the better choice for larger applications.

            And, I’d say I am not alone in this thinking. It is why EC6 now has “Classes”, not to mention all the larger JS frameworks are OO too. ;-)

            I am not sure what I am discussing any more. So, nevermind me.

            Scott

          • markbrown4

            I disagree that OOP helps an application grow and improve over time, as I described earlier I only tend to use them when I need to make multiple instances of something. I would prefer to export shared functions into another module that Flickr and Instagram could consume rather than inheritance, or I might prefer a small amount of duplication there if the code is straight-forward to read. I tend to abstract late, when duplication is getting me down :)

            ES6 didn’t add classes because they are better :) It gave us nothing new in fact, it’s just made constructor functions and prototypes much easier to work with.

  • Steve Rogers

    Thanks for the link. I’m going to do some research. I appreciate it.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in JavaScript, once a week, for free.