DailyJS

Let's Make a Framework: Functional Programming

Alex R. Young

Subscribe

@dailyjs

Facebook

Google+

tutorials frameworks functional web lmaf

Let's Make a Framework: Functional Programming

Posted by Alex R. Young on .
Featured

tutorials frameworks functional web lmaf

Let's Make a Framework: Functional Programming

Posted by Alex R. Young on .

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: