Tidying Up a JavaScript Application with Higher-Order Functions

Posted on by in Web

Higher-order functions are functions that can do one or both of the following:

  1. Take a function or functions as arguments
  2. Return a function as an argument

Most programmers by now are familiar with higher-order functions such as map, reduce, and filter. These functions abstract away the boilerplate when processing collections. For example…

Given a string-capitalizing function and an array of arrays of strings:

function capitalize(s) {
  return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
}

var fruitsOfTheWorld = [
  ["apple", "pineapple", "pear"],
  ["manzana", "pera", "piña"],
  ["poma", "perera", "ananàs"]
];

…we can turn this:

var results = [], 
    i = fruitsOfTheWorld.length;

while (i--) {
  results.unshift([]);
  var j = fruitsOfTheWorld[i].length;
  while (j--) {
    results[0].unshift(capitalize(fruitsOfTheWorld[i][j]));
  }
}

return results;

…into this:

return fruitsOfTheWorld.map(function(fruits) {
  return fruits.map(capitalize);
});

In this post, I’ll demonstrate the usage of higher-order functions outside of a collection-processing context – with the ultimate goal of reducing boilerplate code in your real-world applications (as filter did above). We’ll start with partial function application (facilitated by Underscore’s _.partial) and move on to writing our own higher-order functions.

What is Partial Function Application?

function sum(x, y) {
  return x + y;
}

Given the code above, partial application refers to the application of the function sum to between 0 and n-1 arguments, where n is equal to the number of parameters declared by the function. The result of partially applying a function to arguments is a new function with some (or none) of its parameters filled in with values:

var sumWithXFixedToTen = _.partial(sum, 10);

(typeof sumWithXFixedToTen); // Function

The name sumWithXFixedToTen is bound to a new function returned by _.partial that behaves like sum – but where x will always be the value 10 (that is to say, sumWithXFixedToTen(y) equals sum(10, y) for all y). Moving forward, this new function can be fully-applied with just a single argument (since sum was already partially-applied to 10).

sumWithXFixedToTen(20); // 30
sumWithXFixedToTen(99); // 109

Implementing Partial Function Application

To better understand what is going on behind-the-scenes of libraries like Underscore, we will now work through our own implementation of partial:

function partial(fx) {
  var firstArgs = Array.prototype.slice.call(arguments, 1);

  return function fx2() {
    var secondArgs = Array.prototype.slice.call(arguments, 0);
    return fx.apply(null, firstArgs.concat(secondArgs));
  };
}

Our higher-order partial function:

  1. Takes as its first argument, a function fx to partially apply
  2. Takes as its 2nd…n arguments (it is variadic), values to which fx will be partially applied (firstArgs)
  3. Returns a new function fx2 that, when called, returns the result of applying fx to the both firstArgs and secondArgs

This version of partial provides a convenient way of applying a function to some number of arguments – but lacks some of the nice features found in similar implementations provided by Underscore’s partial and bind, such as preservation of execution context and parameter-skipping. For the rest of this article, I’ll be using Underscore’s functions. For the curious, take a look at their implementation to see what ours is missing.

Real-World Higher-Order Functions

Eliminating Anonymous Function Expressions

When working with libraries like async, programmers often find themselves relying on function expressions to shim application code into the signatures required by waterfall, series, and the like. Partial application of async-agnostic functions to known arguments can eliminate the need for these shims, significantly reducing overall code volume.

For a concrete example, let’s take a look at some real-life client code I came across today:

function createAccount(email, password, done) {
  var id = uuid();

  async.waterfall([
    function(callback) {
      Services.Accounts.provision(email, password, callback);
    }, 
    function(accountId, callback) {
      Services.Account.enable(email, accountId, callback);
    }
  ],
  function(err, auth) {
    done(err, id, auth);
  });
}

Using Underscore, we can eliminate the anonymous function expressions completely:

function createAccount(email, password, done) {
  var id = uuid();

  async.waterfall([
    _.partial(Services.Accounts.provision, email, password),
    _.partial(Services.Notification.confirm, email)
  ],
  _.partial(done, _, id, _));
}

Something to note about Underscore’s partial function is that it can accept a placeholder _ which indicates that an argument should not be pre-filled – but rather provided at call time. In our example, the done function has had only its id parameter pre-filled, leaving err and auth to be provided once our series of asynchronous operations completes.

Higher-Order Error Handling

A pet-peeve of mine is seeing Node.js-style error handling sprinkled all over a codebase:

expressRouter.get("/api/ledgers/:id", function(req, res) {
  UserService.getLedgerById(req.params.id, function(err, ledger) {
    if (err) {
      res.status(404).json({"message": "Not Found"});
    }
    else {
      res.status(200).json(ledger);
    }
  });
});

expressRouter.patch("/api/users/:id", function(req, res) {
  UserService.getUserById(req.params.id, function(err, user) {
    if (err) {
      res.status(404).json({"message": "Not Found"});
    }
    else {
      UserService.updateUser(id, req.body, function(err, user) {
        if (err) {
          res.status(400).json({"message": "Invalid data provided"});
        }
        else {
          res.status(200).json(user);
        }
      });
    }
  });
}

Notice a pattern here? In the asynchronously-executed callbacks that we provide for both getUserById and updateUser, we check the value bound to the first parameter of each callback (err) and respond with some error code and response if its value is truthy. This pattern of checking for and responding to errors can be abstracted into a higher-order function, leaving the application’s happy path free of crufty control logic.

function ensure(notOkCode, notOkMessage, response, okFx) {
  return function(err) {
     if (err) {
       response.status(notOkCode).json({"message": notOkMessage});
     }
     else {
       okFx.apply(okFx, Array.prototype.slice.call(arguments, 1));
     }
  };
}

var ensureFound = _.partial(ensure, 404, "Not Found"),
    ensureValid = _.partial(ensure, 400, "Bad Request");

expressRouter.get("/api/ledgers/:id", function(req, res) {
  UserService.getLedgerById(req.params.id, ensureFound(res, function(ledger) {
    res.status(200).json(ledger);
  }));
});

expressRouter.patch("/api/users/:id", function(req, res) {
  UserService.getUserById(req.params.id, ensureFound(res, function(user) {
    UserService.updateUser(user, req.body, ensureValid(res, function(updated) {
      res.status(200).json(updated);
    }));
  }));
});

Takeaways

Higher-order functions will help you write JavaScript with less cruft. By familiarizing yourself with Underscore’s partial function (or something you write yourself), you’ll have one more tool for factoring out common patterns into reusable, higher-order little chunks. Functional programming FTW!

Related Reads

  1. Composing Asynchronous with Synchronous Functions in JavaScript
  2. Gettin’ Freaky Functional w/Curried JavaScript

Feedback

  Comments: 5


  1. Personally, I love this – its so easy to read the top-level code and understand its intent. However, I suspect maintaining this code will be difficult for a majority of JavaScript coders – you could be making it more expensive to hire your replacement.

    • Erin Swenson-Healey


      Hi Bruce Williams,

      Thanks for the comment. Out of curiosity – what about the code in this article strikes you as particularly hard to maintain?

      Take care,

      Erin


      • Basically any extension of this code that involves understanding how the functions and associated execution context get created. For example, adding one error handler for multiple HTTP status codes. You would likely find it trivial, but I’ve worked with folks who would struggle doing that, at least in a clean way.


  2. Great writeup on higher level functions, as this style of programming is becoming quite common now with Angular et al, it’s good to get an overview on it. If anyone struggles to understand this, have a look at this first: http://www.onmedia.net.au/javascript-101-functions-and-iifes/


  3. Removing for/while loops is my new favorite, I hope It doesn’t hurt readability!

Your feedback