Jun 4, 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

Bilby

A self-contained functional library, Brian McKenna’s Bilby stretches the possibilities of functional style in JavaScript. It’s worth exploring Bilby to learn its entire feature set, but one that is particularly nice is its implementation of multimethods.4 Brian touts his use of category theory in Bilby, but that’s a red herring that detracts from its powerful, yet simple, capabilities and ideas.

For example, with Bilby is you can define functions that dispatch on any number of interesting conditions. Bilby provides a module system called environments that aggregate related methods and properties:

var noAnimals = bilby.environment();

Before adding a multimethod I can define a few helper functions:

function voice(type, sound) { return ["The", type, "says", sound].join(' '); } function isA(thing) { return function(obj) { return obj.type == thing; } } function say(sound) { return function(obj) { console.log(voice(obj.type, sound)); } }

Using these helpers I can tell Bilby:

The name of the method A predicate that checks the arguments5 An action function that performs the method behaviors

The Environment#method takes the three arguments listed above and returns a new environment:

var cats = noAnimals.method('speak', isA('cat'), say("mew"));

As shown, adorning an environment with a new multimethod returns a new environment. I can now call speak :

cats.speak({type: 'cat'}); // (console) The cat says mew

Adding a new polymorphic behavior is simple:

var catsNDogs = cats.method('speak', isA('dog'), say("woof"));

And calling speak with a dog object works as expected:

catsNDogs.speak({type: 'dog'}); // (console) The dog says woof

And cats still sound like cats:

catsNDogs.speak({type: 'cat'}); // (console) The cat says mew

Of course, I can match on an arbitrary condition within the dispatch predicate:

var animals = catsNDogs.method('speak', function(obj) { return isA('frog')(obj) && (obj.status == 'dead'); }, say('Hello ma, baby!'));

So passing in a dead frog works all the same:

animals.speak({type: 'frog', status: 'dead'}); // (console) The frog says Hello ma, baby!

I love the idea of the immutable environments and look forward to the chance to take advantage of them for a project. Of course, Bilby provides more than multimethods, including a trampoline that allows you to return functions, monadic structures,6, a crazy-insane operator overloading form, validation helpers and much more.

If you want to talk functional programming, my book, the library in question or anything else then feel free to comment below or email me at the address at the top of this blog post.

:F