It's been said that every functional programmer writes their own library of functions, and functional JavaScript programmers are no exception. With today's open source code-sharing platforms such as GitHub, Bower, and NPM, it's easier to share, collaborate, and grow these libraries. Many libraries exist for functional programming with JavaScript, ranging from tiny toolkits to monolithic module libraries.
Each library promotes its own style of functional programming. From a rigid, math-based style to a relaxed, informal style, each library is different but they all share one common feature: they all have abstract JavaScript functional capabilities to increase code re-use, readability, and robustness.
At the time of writing, however, a single library has not established itself as the de-facto standard. Some might argue that underscore.js
is the one but, as you'll see in the following section, it might be advisable to avoid underscore.js
.
Underscore has become the standard functional JavaScript library in the eyes of many. It is mature, stable, and was created by Jeremy Ashkenas, the man behind the Backbone.js
and CoffeeScript
libraries. Underscore is actually a reimplementation of Ruby's Enumerable
module, which explains why CoffeeScript was also influenced by Ruby.
Similar to jQuery, Underscore doesn't modify native JavaScript objects and instead uses a symbol to define its own object: the underscore character "_
". So, using Underscore would work like this:
var x = _.map([1,2,3], Math.sqrt); // Underscore's map function console.log(x.toString());
We've already seen JavaScrip's native map()
method for the Array
object, which works like this:
var x = [1,2,3].map(Math.sqrt);
The difference is that, in Underscore, both the Array
object and the callback()
function are passed as parameters to the Underscore object's map()
method (_.map
), as opposed to passing only the callback to the array's native map()
method (Array.prototype.map
).
But there's way more than just map()
and other built-in functions to Underscore. It's full of super handy functions such as find()
, invoke()
, pluck()
, sortyBy()
, groupBy()
, and more.
var greetings = [{origin: 'spanish', value: 'hola'}, {origin: 'english', value: 'hello'}]; console.log(_.pluck(greetings, 'value') ); // Grabs an object's property. // Returns: ['hola', 'hello'] console.log(_.find(greetings, function(s) {return s.origin == 'spanish';})); // Looks for the first obj that passes the truth test // Returns: {origin: 'spanish', value: 'hola'} greetings = greetings.concat(_.object(['origin','value'], ['french','bonjour'])); console.log(greetings); // _.object creates an object literal from two merged arrays // Returns: [{origin: 'spanish', value: 'hola'}, //{origin: 'english', value: 'hello'}, //{origin: 'french', value: 'bonjour'}]
And it provides a way of chaining methods together:
var g = _.chain(greetings) .sortBy(function(x) {return x.value.length}) .pluck('origin') .map(function(x){return x.charAt(0).toUpperCase()+x.slice(1)}) .reduce(function(x, y){return x + ' ' + y}, '') .value(); // Applies the functions // Returns: 'Spanish English French' console.log(g);
Despite its ease of use and adaptation by the community, the underscore.js
library has been criticized for forcing you to write overly verbose code and for encouraging the wrong patterns. Underscore's structure may not be ideal or even function!
Until version 1.7.0, released shortly after Brian Lonsdorf's talk entitled Hey Underscore, you're doing it wrong!, landed on YouTube, Underscore explicitly prevented us from extending functions such as map()
, reduce()
, filter()
, and more.
_.prototype.map = function(obj, iterate, [context]) { if (Array.prototype.map && obj.map === Array.prototype.map) return obj.map(iterate, context); // ... };
You can watch the video of Brian Lonsdorf's talk at www.youtube.com/watch?v=m3svKOdZij.
Map, in terms of category theory, is a homomorphic functor interface (more on this in Chapter 5, Category Theory). And we should be able to define map
as a functor for whatever we need it for. So that's not very functional of Underscore.
And because JavaScript doesn't have built-in immutable data, a functional library should be careful to not allow its helper functions to mutate the objects passed to it. A good example of this problem is shown below. The intention of the snippet is to return a new selected
list with one option set as the default. But what actually happens is that the selected
list is mutated in place.
function getSelectedOptions(id, value) { options = document.querySelectorAll('#' + id + ' option'); var newOptions = _.map(options, function(opt){ if (opt.text == value) { opt.selected = true; opt.text += ' (this is the default)'; } else { opt.selected = false; } return opt; }); return newOptions; } var optionsHelp = getSelectedOptions('timezones', 'Chicago');
We would have to insert the line opt = opt.cloneNode();
to the callback()
function to make a copy of each object within the list being passed to the function. Underscore's map()
function cheats to boost performance, but it is at the cost of functional feng shui. The native Array.prototype.map()
function wouldn't require this because it makes a copy, but it also doesn't work on nodelist
collections.
Underscore may be less than ideal for mathematically-correct, functional programming, but it was never intended to extend or transform JavaScript into a pure functional language. It defines itself as a JavaScript library that provides a whole mess of useful functional programming helpers. It may be a little more than a spurious collection of functional-like helpers, but it's no serious functional library either.
Is there a better library out there? Perhaps one that is based on mathematics?
Sometimes, the truth is stranger than fiction.
Fantasy Land is a collection of functional base libraries and a formal specification for how to implement "algebraic structures" in JavaScript. More specifically, Fantasy Land specifies the interoperability of common algebraic structures, or algebras for short: monads, monoids, setoids, functors, chains, and more. Their names may sound scary, but they're just a set of values, a set of operators, and some laws it must obey. In other words, they're just objects.
Here's how it works. Each algebra is a separate Fantasy Land specification and may have dependencies on other algebras that need to be implemented.
Some of the algebra specifications are:
equals()
methodconcat()
methodempty()
methodmap()
methodThe list goes on and on.
We don't necessarily need to know exactly what each algebra is for but it certainly helps, especially if you're writing your own library that conforms to the specifications. It's not just abstract nonsense, it outlines a means of implementing a high-level abstraction called category theory. A full explanation of category theory can be found in Chapter 5, Category Theory.
Fantasy Land doesn't just tell us how to implement functional programming, it does provide a set of functional modules for JavaScript. However, many are incomplete and documentation is pretty sparse. But Fantasy Land isn't the only library out there to implement its open source specifications. Others have too, namely: Bilby.js.
What the heck is a bilby? No, it's not a mythical creature that might exist in Fantasy Land. It exists here on Earth as a freaky/cute cross between a mouse and a rabbit. Nonetheless, bibly.js
library is compliant with Fantasy Land specifications.
In fact, bilby.js
is a serious functional library. As its documentation states, it is, Serious, meaning it applies category theory to enable highly abstract code. Functional, meaning it enables referentially transparent programs. Wow, that is pretty serious. The documentation located at http://bilby.brianmckenna.org/ goes on to say that it provides:
By far the most mature library that conforms to the Fantasy Land specifications for algebraic structures, Bilby.js
is a great resource for fully committing to the functional style.
Let's try an example:
// environments in bilby are immutable structure for multimethods var shapes1 = bilby.environment() // can define methods .method( 'area', // methods take a name function(a){return typeof(a) == 'rect'}, // a predicate function(a){return a.x * a.y} // and an implementation ) // and properties, like methods with predicates that always // return true .property( 'name', // takes a name 'shape'); // and a function // now we can overload it var shapes2 = shapes1 .method( 'area', function(a){return typeof(a) == 'circle'}, function(a){return a.r * a.r * Math.PI} ); var shapes3 = shapes2 .method( 'area', function(a){return typeof(a) == 'triangle'}, function(a){return a.height * a.base / 2} ); // and now we can do something like this var objs = [{type:'circle', r:5}, {type:'rect', x:2, y:3}]; var areas = objs.map(shapes3.area); // and this var totalArea = objs.map(shapes3.area).reduce(add);
This is category theory and ad-hoc polymorphism in action. Again, category theory will be covered in full in Chapter 5, Category Theory.
The truth is that Bilby and Fantasy Land are really stretching the possibilities of functional programming in JavaScript. Although it's exciting to see the evolution of computer science, the world may just not be ready for the kind of hard-core functional style that Bibly and Fantasy Land are pushing.
Maybe such a grandiose library on the bleeding-edge of functional JavaScript is not our thing. After all, we set out to explore the functional techniques that complement JavaScript, not to build functional programming dogma. Let's turn our attention to another new library, Lazy.js
.
Lazy is a utility library more along the lines of the underscore.js
library but with a lazy evaluation strategy. Because of this, Lazy makes the impossible possible by functionally computing results of series that won't be available with immediate interpretation. It also boasts a significant performance boost.
The Lazy.js
library is still very young. But it has a lot of momentum and community enthusiasm behind it.
The idea is that, in Lazy, everything is a sequence that we can iterate over. Owing to the way the library controls the order in which methods are applied, many really cool things can be achieved: asynchronous iteration (parallel programming), infinite sequences, functional reactive programming, and more.
The following examples show off a bit of everything:
// Get the first eight lines of a song's lyrics var lyrics = "Lorem ipsum dolor sit amet, consectetur adipiscing eli // Without Lazy, the entire string is first split into lines console.log(lyrics.split(' ').slice(0,3)); // With Lazy, the text is only split into the first 8 lines // The lyrics can even be infinitely long! console.log(Lazy(lyrics).split(' ').take(3)); //First 10 squares that are evenly divisible by 3 var oneTo1000 = Lazy.range(1, 1000).toArray(); var sequence = Lazy(oneTo1000) .map(function(x) { return x * x; }) .filter(function(x) { return x % 3 === 0; }) .take(10) .each(function(x) { console.log(x); }); // asynchronous iteration over an infinite sequence var asyncSequence = Lazy.generate(function(x){return x++}) .async(100) // 0.100s intervals between elements .take(20) // only compute the first 20 .each(function(e) { // begin iterating over the sequence console.log(new Date().getMilliseconds() + ": " + e); });
More examples and use-cases are covered in Chapter 4, Implementing Functional Programming Techniques in JavaScript.
But its not entirely correct to fully credit the Lazy.js
library with this idea. One of its predecessors, the Bacon.js
library, works in much the same way.
The logo of Bacon.js
library is as follows:
The mustachioed hipster of functional programming libraries, Bacon.js
is itself a library for functional reactive programming. Functional reactive programming just means that functional design patterns are used to represent values that are reactive and always changing, like the position of the mouse on the screen, or the price of a company's stock. In the same way that Lazy can get away with creating infinite sequences by not calculating the value until it's needed, Bacon can avoid having to calculate ever-changing values until the very last second.
What are called sequences in Lazy are known as EventStreams and Properties in Bacon because they're more suited for working with events (onmouseover
, onkeydown
, and so on) and reactive properties (scroll position, mouse position, toggles, and so on).
Bacon.fromEventTarget(document.body, "click") .onValue(function() { alert("Bacon!") });
Bacon is a little bit older than Lazy but its feature set is about half the size and its community enthusiasm is about equal.
There are simply too many libraries out there to do them all justice within the scope of this book. Let's look at a few more libraries for functional programming in JavaScript.
Functional
wu.js
curryable()
function, wu.js
library is a very nice Library for functional programming. It was the first library (that I know of) to implement lazy evaluation, getting the ball rolling for Bacon.js
, Lazy.js
and other librariessloth.js
stream.js
Lo-Dash.js
Sugar
Sugar
is a support library for functional programming techniques in JavaScript, like Underscore, but with some key differences in how it's implemented._.pluck(myObjs, 'value')
in Underscore, it's just myObjs.map('value')
in Sugar. This means that it modifies native JavaScript objects, so there is a small risk of it not playing nicely with other libraries that do the same such as Prototype.from.js
Boiler.js
$('#mydiv').fadeIn().css('left': 50).alert('hi!');
A full explanation of this can be found in Chapter 30, Functional and Object-oriented Programming in JavaScript.
$('li').css('left': function(index){return index*50});
deferred.then
parameter implements a functional concept known as Promises.