Functional Javascript for OO developers, Part 1

Functional Javascript for OO developers, Part 1

Javascript can be a weird thing to a traditional OO developer. For small tasks it's easy enough to pick up and hack around with, with it's familiar looking syntax and the fairly forgiving nature of most browsers. But when you try to do something larger than a couple of dozen lines you realise that the familiarity is only skin deep.

Javascript is the antithesis of traditional OO languages like C++, Pascal, Java and C#. Where they are static, strongly typed, imperative languages with classical inheritance; it is dynamic, weakly typed, with prototypical inheritance. Although it can be used imperatively, Javascript has many of the features of a functional language. And on top of that it has a bunch of quirks that confuse the crap out of most people.

In short, it's its own beast. But it's a beast that is quite a lot of fun, and well worth learning. This is particularly true of its functional aspects.

In part 1 of this article, I'll briefly introduce functional concepts in Javascript from the point of view of an OO developer. In part 2 I'll go through a detailed comparison of how you would approach the same problem from OO and functional perspectives.

Why does functional Javascript matter?

When they first play with it, most OO developers will try to architect their Javascript in an OO way. There are hundreds of articles and books on how to get prototypical inheritance kinda/sorta working like a class, CoffeeScript does it automagically for you, and the ECMAScript panel are even thinking of including class-type stuff in future versions of the language itself. So you'd be by no means alone if you went down that path; I've certainly been there.

But, even with the helpers and the potential language changes, classes in Javascript just seem to... suck. There's all this hand-wavey orchestration required to construct them safely, but in the end I'm still not sure they give you much, other than the ability keep mentally modelling the world in a way you're familiar with.

Stop solving problems using classes, and not having classing will stop being a problem

Don't get me wrong: OO has its advantages and it can be a helpful way to think about a problem. Dynamic OO languages like Python and Ruby address some of the deep inheritance insanity that can come with statically typed languages. But there are other ways to model problems, and the more I use it, the more I like the functional approach.

Lets get functional

This article came out of my own frustrating experience of trying to solve a problem in Javascript using OO design. I started to read up on functional design approaches and found it fun and interesting, and it lead to a more satisfactory solution.

In the first year of my IT degree, a maths professor attempted to teach us functional programming (in Miranda, an intellectual precursor of Haskell). He was very excited about being able to prove the mathematical correctness of algorithms; most of us stared blankly and didn't take much in. It certainly went well over my head, despite having done well in maths in High School. The next year they moved the course to third year. That experience, and the lack of industry use, turned me off functional programming for a long time.

Fast forward a few(!) years, and Microsoft adds LINQ to objects to C# 3.0. LINQ is a beautiful thing. It allows you to perform all sorts of set-based transformations by passing anonymous functions (in the form of lambda expressions). It's a bit weird when you first see it, but soon you can't live without it. For example, if you have a list of objects, and you want to create a new list with certain properties for each object, you could do it like this:

var newList = new List<string>();
foreach(var item in list)
    newList.Add(item.Name);

With LINQ, you can do it like this

var newList = list.Select(x => x.Name);

And there's a whole bunch of LINQ methods: Where(), First(), Any(), Count(), SelectMany(), Aggregate(). Things get more fun when you chain them together:

var adultNames = people.Where(x => x.Age > 18).Select(x => x.Name);
var distinctTagCount = posts.SelectMany(p => p.Tags).Distinct().Count();

It may seem like syntactic sugar, and to a degree it is. But so is almost everything useful about modern languages (otherwise we'd still all be writing C). But more than just sugar, LINQ starts to change the way you think: from "how do I get what I want" to "what do I want". That is, from imperative to functional thinking.

Microsoft has surreptitiously been teaching OO programmers to think functionally

Unsurprisingly, it turns our that LINQ is inspired by good old functional languages, arguably by way of Ruby. Select() is the equivalent of a functional language's map(); Aggregate() is reduce(), SelectMany() is flatten(map()).

Where C# has these "what do I want" methods for collections of certain types, functional languages take this approach with everything. Where imperative (and OO) languages concern themselves with manipulating state, functional languages concern themselves with answering questions.

Which brings us back to...

Javascript. Where classes in Javascript are ugly hacks, functional design feels very at home. This is doubly the case if you use underscore.js, a library which provides many of the standard "functional" functions. But even without underscore, it's easy to roll your own. Lets do that, to get into the functional flow.

First up, we're going to need some sort of array iterator. We can do this by wrapping up Javascript's for:

function each(list, func){
    for(var i = 0; i < list.length; i++)
        func(list[i]);
}

This is simple enough, unless you're not used to seeing functions passed as arguments (hold on tight, we'll do that a lot). If you're really keen, you can implement each() in a more 'functional' style with a recursive function rather than relying on the more imperative-style for loop.

We can use each() to build up some other useful functions, like a conditional count:

function count(list, predicate){
    if(!predicate)
        return list.length;

    var result = 0;    
    each(list, function(x){if (predicate(x)) result++});

    return result;
}

The predicate here is a function that takes a list item and returns a boolean: if it evaluates to true the count is incremented, otherwise it's not. If you don't supply a predicate we substitute a function that always returns true, counting all the items. Here's how it would be used:

var a = [1,2,3,4];
count(a); // 4
count(a, function(x){ return x > 1}); // 3

The ability to pass functions around as arguments and return values is known as having 'first class functions', and is a core feature of functional languages. Unfortunately Javascript's anonymous function syntax isn't as clean as, say, C#'s lambda syntax, but it is still quite readable.

You'll also notice that we reference result in our anonymous function, even though it is declared in the outer count() function. This works because of a funky functional concept called closure. In essence, the anonymous function 'closes over' the variable, taking a reference with it whereever it goes and keeping it in scope. In some cases this can mean that a variable remains in scope longer than the function that declared it. It sounds weird, but is immensely useful.

You can see it in use again here in the implementation of map():

function map(list, func){
    var values = [];

    each(list, function(x){ values.push(func(x))});

    return values;
}

Closures allow our first class functions to do rather useful things: it would be much harder to implement a generically useful each() if the function you gave it couldn't manipulate a data structure defined in the calling function.

Next Time...

By now you should be getting a feel for how you can construct small, specific functions that answer a question ("what are the names of all the people 18 years or older") rather than modify state. But it's probably still not clear how you organise and orchestrate them to solve a problem that you may have traditionally modelled as classes and interfaces. I'll cover that in the next post.

comments powered by Disqus