read


read
or learn more

Fun.js – Just a sketch

Jun 6, 2013

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.3

In this installment I’m just going to sketch a functional JavaScript library that does not exist, but that I wish did4… 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.


  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

  3. Seriously, who needs yet another blog post on closures, map, reduce, filter, __proto__ and the like? I’ll hit on those topics from time to time, but they will not be the focus of these posts. 

  4. Undermine, if it did exist would need more than this, but the spirit of my desires are represented herein. 

4 Comments, Comment or Ping

  1. Aleksey Kulikov

    I love this article! It seems, that this approach can be perfectly implemented in component.js philosophy. Create small tool libraries to work with js in functional way. I would want to have this amazing map, reduce, filter and pipeline available.

  2. @Aleksey

    The pipeline function is from underscore-contrib so you can start using it right away. The curried versions of the “big 3” are not available anywhere (someone correct me if I’m wrong), but they’re easy enough to add. The real trick is to build a library with this kind of philosophy at its core.

  3. Aleksey Kulikov

    @fogus

    Actually your idea about improving underscore with better currying is very close to this talk: http://www.youtube.com/watch?v=m3svKOdZijA and this guy started library with close to your name: scoreunder :)

    The problem with underscore and underscore-contrib, that it’s too much monolithic, it is all in one, but often you just need a map or filter.

    I mentioned component.js as a good idea to create small well tested js-libraries, that do one thing well. Take a look at the 1000 existing components https://github.com/component/component/wiki/Components, but they still don’t have a good map.

  4. Phil

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

    greaterThan(5, 100); //=> true

    ?

Reply to “Fun.js – Just a sketch”