Let's Make a Framework: Functional Programming
Welcome to part 4 of Let’s Make a Framework, the ongoing series about building a JavaScript framework. This part introduces the functional part of the library, with a brief introduction to functional programming.
If you haven’t been following along, these articles are tagged with lmaf. The project we’re creating is called Turing and is available on GitHub: turing.js.
Functional Programming 101
The name functional programming is annoying because it reminds novices of procedural programming, which people learn first then discard in favour of object oriented programming. Forget all that for a bit.
Functional programming is about:
- Describing problems rather than focusing on the mechanics of their solution
- Treating functions as first class citizens, and manipulating them like variables
- Avoiding state and mutable data
There are functional programming languages like Erlang and Haskell. JavaScript isn’t strictly functional, but neither are Ruby, Python, or Java. However, these languages use ideas from functional programming to simplify common programming tasks.
Functional languages usually focus on lists, and treat functions as first class and have useful features like closures. JavaScript is actually pretty good at functions and closures, and JavaScript arrays and objects are similar to lists and property lists in lisp-like languages.
I once saw a talk by Tom Stuart called Thinking Functionally in Ruby. It has some animations that really make functional programming paradigms easy to understand. If you’re still finding the concept nebulous try watching his presentation.
Iterators
JavaScript frameworks use each a lot. Some frameworks define classes early on, but almost all define this basic tool for iteration.
Ruby programmers use each all the time:
[1, 2, 3].each { |number| puts number }
# 1
# 2
# 3
This sends a block to each, and each runs the block multiple times. Enumerable uses each to create lots of other methods that are inspired by functional languages. Any collection-style object can mixin Enumerable to get all those methods for free.
Equivalent JavaScript could look like this:
Array.prototype.each = function(callback) {
for (var i = 0; i < this.length; i++) {
callback(this[i]);
}
}
[1, 2, 3].each(function(number) {
print(number);
});
However, JavaScript actually has Array.forEach, Array.prototype.forEach, for (var i in objectWithIterator) and even more ways to iterate. So why do frameworks bother defining their own method? One of the reasons you see jQuery.each and each in Prototype is because browser support is inconsistent.
You can see the source for jQuery’s each in core.js.
Prototype’s implementation uses forEach if it exists:
(function() {
var arrayProto = Array.prototype,
slice = arrayProto.slice,
_each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
Underscore uses a similar approach:
// The cornerstone, an each implementation.
// Handles objects implementing forEach, arrays, and raw objects.
// Delegates to JavaScript 1.6's native forEach if available.
var each = _.forEach = function(obj, iterator, context) {
try {
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (_.isNumber(obj.length)) {
for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
} else {
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
}
}
} catch(e) {
if (e != breaker) throw e;
}
return obj;
};
This approach uses JavaScript’s available datatypes and features, rather than mixing an Enumerable-style class into objects and Array.
Benchmarks
I’ve written some benchmarks to test each implementation. You can view them here: test.html, and iteratortest.js.
Each will form a cornerstone of Turing’s functional programming features, so let’s create a benchmark to see if the native function is really faster.
| Rhino | Node | Firefox | Safari | Chrome | Opera | IE8 | IE7 | IE6 | |
| eachNative | 1428ms | 69ms | 709ms | 114ms | 62ms | 1116ms | |||
| eachNumerical | 2129ms | 55ms | 904ms | 74ms | 58ms | 1026ms | 3674ms | 10764ms | 6840ms |
| eachForIn | 4223ms | 309ms | 1446ms | 388ms | 356ms | 2378ms | 4844ms | 21782ms | 14224ms |
The native method performs well, and generally close to the simple for loop. This probably explains why most JavaScript library implementors use it when it’s there. And for ... in performs so terribly in Internet Explorer that we need to be careful about using it.
API Design
An important consideration is the API design for functional features. The Prototype library modifies Object and Array‘s prototypes. This makes the library easy to use, but makes it difficult to use concurrently with other libraries: it isn’t safely namespacing.
Underscore has clearly namespaced design, with optional use for what it calls functional or object-oriented (which allows chained calls).
Our library could look like this:
turing.enumerable.each([1, 2, 3], function(number) { number + 1; });
turing.enumerable.map([1, 2, 3], function(number) { return number + 1; });
// 2, 3, 4
These methods could be mapped to shorthands later on.
Tests
A basic test of each and map should ensure arrays are iterated over:
Riot.context('turing.enumerable.js', function() {
given('an array', function() {
var a = [1, 2, 3, 4, 5];
should('iterate with each', function() {
var count = 0;
turing.enumerable.each(a, function(n) { count += 1; });
return count;
}).equals(5);
should('iterate with map', function() {
return turing.enumerable.map(a, function(n) { return n + 1; });
}).equals([2, 3, 4, 5, 6]);
});
});
Objects should also work:
given('an object', function() {
var obj = { one: '1', two: '2', three: '3' };
should('iterate with each', function() {
var count = 0;
turing.enumerable.each(obj, function(n) { count += 1; });
return count;
}).equals(3);
should('iterate with map', function() {
return turing.enumerable.map(obj, function(n) { return n + 1; });
}).equals(['11', '21', '31']);
});
The resulting implementation is based heavily on Underscore, because it’s easy to understand and my benchmarks show it’s pretty smart. View the code here: turing.enumerable.js
Conclusion
In this part of Let’s Make a Framework, I’ve introduced the basic premise of functional programming. The key to this in JavaScript is each, which will let us implement lots of useful methods to use on collections. I’ve benchmarked different ways of implementing each and discussed how different libraries make this functionality available.
You might remember me mentioning mutable data. Some programming languages don’t let you modify data, so everything changed becomes a copy. Optimisation can actually make this fast, and in some cases, due to the advantages of lazy evaluation, performance isn’t a great problem. I’ve previously tried to implement lazy data types in JavaScript, so I’ll see if I can work this into the framework in the future.
Further reading:
- For a different iterator implementation, try callbacks and for
- Underscore has great documentation
- Iterators in JavaScript 1.7
- How many ways can you iterate?
- Clojure is predominantly functional and should appeal to JavaScripters