I’ve written a book entitled Functional JavaScript due out in print sometime in June 2013. In the book I introduce and explore various techniques for writing code in a functional style using the Underscore library. As much as I would have loved to delve into the ecosystem of functional JS libraries, I didn’t want to distract from the core functional topics in the book.1 Therefore, I present a series to explore a few of my favorite functional JavaScript libraries2 focusing primarily on the feature(s) that make them interesting, innovative or unique.[^map]

In this installment I’m just going to sketch a functional JavaScript library that does not exist, but that I wish did[^maybe]… for fun I’ll call it Undermine.

I’ll start it like I start all of my JS libraries:

var exists = function(x) { return x != null; };

var truthy = function(x) { 
  return (x !== false) && exists(x); 
};

These two functions are just meant to simplify JS truthiness. The library itself starts with filter:

var _filter = function(fun) {
  return function(coll) {
    var results = [];
    if (coll == null) return results;
    coll.forEach(function(value, index, list) {
      if (truthy(fun.call(null, value))) results.push(value);
    });
    return results;
  };
};

function filter(fun) {
  var coll = arguments[1];

  if (exists(coll)) return _filter(fun)(coll);

  return _filter(fun);
}

… and then map:

var _map = function(fun) {
  return function(coll) {
    var results = [];
    if (coll == null) return results;
    coll.forEach(function(value, index, list) {
      results.push(fun.call(null, value));
    });
    return results;
  };
};

function map(fun) {
  var coll = arguments[1];

  if (exists(coll)) return _map(fun)(coll);

  return _map(fun);
}

To see map in action just run the following:

function double(n) { return 2*n; }
 
map(double, [1,2,3])
//=> [2, 4, 6]

But both filter and map are implemented in a funny way. That is, they only return a result if all of the expected arguments are given, otherwise they return a closure that expects a collection. This is called currying. Let’s see it in action:

var doubleAll = map(double);
 
doubleAll([1,2,3]);
//=> [2, 4, 6]

Seems pretty straight-forward no? Although I curry these 2-arg functions manually, I can write a function to curry 2-arg functions automatically:

function schonfinkel(fun) {
  return function(first) {
    return function(last) {
      return fun(first, last);
    };
  };
};

The schonfinkel function curries a function of two parameters, from left to right.

Oh look! Here’s a function:

function greaterThan(l, r) {
  return l > r;
}

greaterThan(500, 10);
//=> true

Here’s a curried version:

var maybeGT = schonfinkel(greaterThan);

And its usage:

maybeGT(5)
//=> function(last) { ... }
 
maybeGT(5)(10000000);
//=> false

Whoops. The problem is that the curried application ordering is wrong, so let’s create a function to flip the application order:

function flip(fun) {
  return function() {
    return fun.call(null, arguments[1], arguments[0]);
  };
}

And so the new function becomes:

var gt = schonfinkel(flip(greaterThan));
 
gt(5)
//=> function(last) { ... }
 
gt(5)(10000000);
//=> true
 
gt(5)(1);
//=> false

You know what? I forgot reduce. Here it is, following a different currying pattern:

var _reduce = function(fun, seed, coll) {
  var acc = seed;
  coll.forEach(function(value) {
    acc = fun.call(null, acc, value);
  });
  return acc;
};
 
function reduce(fun) {
  var count = arguments.length;
  var seed = arguments[1];
  var coll = arguments[2];
  var result;
 
  switch(count) {
  case 1:
    result = schonfinkel(_reduce.bind(null, fun));
    break;
 
  case 2:
    result = _reduce.bind(null, fun, seed);
    break;
 
  case 3:
    result = _reduce(fun, seed, coll);
    break;
  }
 
  return result;
}

reduce is more complicated and inverted compared to map and filter, but that’s only because I want to show the use of partial application (in the result = schonfinkel(_reduce.bind(null, fun)) snippet). Since reduce assumes that it gets a fun then said fun can be partially applied and the resulting function from Function#bind Schonfinkeled… I mean curried. Partial application is different than currying. A partially applied function is always ready to rock, but a curried function keeps returning closures until the last arg is given, only then is it executable.

Let’s see reduce in action:

function sum(x,y) { return x + y; }
 
reduce(sum, 0, [1,2,3,4,5]);
//=> 15

Seems correct no? What about a curried version:

var summate = reduce(sum, 0);
 
summate([1,2,3,4,5]);
//=> 15

So what’s the point of all this? The goal in a library like Undermine is to strive to facilitate the description of programs as a series of actions that should occur. A function that can help execute such a series is as follows:

var pipeline = function(/*, funs */){
  var funs = Array.prototype.slice.call(arguments);
 
  return function(seed) {
    return reduce(function(l,r) { return r(l); },
                  seed,
                  funs);
  };
};

That’s a bit subtle, but let’s see it in action:

var gimmeTheThingTHETHING = pipeline();

gimmeTheThingTHETHING(5);
//=> 5

So a pipeline that’s given nothing returns a function that just returns whatever’s given it. What about if pipeline is given something?

var doubler = pipeline(double);

doubler(5);
//=> 10

So the closure returned by pipeline calls the captured function argument in the beginning with whatever’s passed into it. What about if I supply more functions?

var quadrupler = pipeline(double, double);

quadrupler(5);
//=> 20

Seems right no? I can now use pipeline to describe a sequence of actions to take to perform some task:

var tasks = pipeline(
  map(double)
  , filter(gt(5))
);
 
tasks([1,2,3,4,5]);
//=> [6,8,10]

The tasks are described as:

  1. First double everything
  2. then take everything >5

And that’s exactly what the code says! Pipelines can also be strung together:

var moreTasks = pipeline(
  tasks
  , reduce(function(x,y) { return x*y }, 1)
);

moreTasks([1,2,3,4,5]);                                                                                                
//=> 480  

Pretty cool huh? That’s all for Undermine. There’s more stuff like this in my book Functional JavaScript.

Enjoy.

:F

Thanks to Michael-Keith for the idea for a better currying approach.

[^map


  1. I cover a few in an appendix, but did not go as deeply as I would have liked given unlimited time and page count.↩︎

  2. Including, but not limited to: Functional JavaScript by Oliver Steele, Lemonad, RxJS, Bilby, Allong.es, D3, Reducers, Underscore-contrib, Mori and Bacon.js.↩︎