Friday, January 20, 2012

JavaScript and Scala: Good Parts and Bad

I've just finished reading "JavaScript: The Good Parts" by Douglas Crockford. It's a fantastic book, mostly because it's incredibly concise, elaborating only the details that experienced programmers should need to understand the true form of JavaScript. If you do ANY JavaScript programming and you haven't read it, I suggest you go out and get it straight away, and don't write a single line more of buggy JS code until you do.

What is 'this'?
Early on in the book, Crockford highlights one of JavaScript's oddest and most dangerous quirks: the late binding of 'this'. Basically, whenever you call a function, the value of 'this' within the function is determined by HOW you called the function!

To demonstrate: If object 'a' has a function called 'run()' and you invoke it like this:
a.run();
then the value of 'this' within 'run()' will be 'a'. JavaScript is a functional language, so functions can be passed around as values. If you instead invoked the function above using only a reference to the function value, like this:
var f = a.run;
f();
then the value of 'this' within 'run()' will be... the global variable pool! As you can imagine, that can lead to some pretty nasty and hard to track bugs.

Functional Limitations
 If you're a fan of functional programming, you might have tweaked straight away that this could place limits on the usefulness of functions as first class objects, and specifically higher-order functions (i.e. functions that take other functions in their parameters).

Here's an example of an object that doesn't work with higher-order functions:
var object1 = {
    total: 0,
    add: function(numbers) {
        var i;
        for (i = 0; i < numbers.length; i++) {
            this.total += numbers[i]
        }
        return this.total;
    }
};

var valueOf = function (f, p1) {
    return f(p1);
}

var numbers = [1, 2, 3, 4];

document.writeln(object1.add(numbers) + " / " + valueOf(object1.add, numbers));
// outputs: 10 / NaN
Obviously the reason this quirk causes confusion is because JavaScript has borrowed a keyword from other languages but uses it to mean something subtly different. Most OO languages use 'this' to represent a pointer to an object in which the function seemingly resides. Importantly, in OO/functional languages, when a function is used as a value, it takes with it a closure around 'this' that is used when invoking it. In JavaScript, however, the 'this' pointer just refers to the object on which the function was called, or if there was no object, just to the global variable pool.

This Naughtiness Explained
There is a reason for this weirdness. Because JavaScript is classless, the function doesn't actually belong to the object in which it resides. In fact, it's not hard to imagine situations where JavaScript's behaviour is what you'd expect. If I took a function from one object and placed it in another object (which I can do, because the function is also a value), I actually would expect 'this' to refer to the new object when I invoked it, and JavaScript provides just that capability:
var b = {};
b.run = a.run;
b.run(); // 'this' will refer to 'b'
Once you understand where JavaScript is coming from a bit better, it's not too hard to see the solution to this problem. 'this' doesn't provide a closure around the context where your function was defined, so if you want a closure around the context where your function was defined, then give your function a closure around that context, rather than assuming that 'this' will be the same context whenever the function is invoked. To demonstrate this by correcting the example above:
var object2 = (function () {
    var that = {};
    that.total =  0;
    that.add = function(numbers) {
        var i;
        for (i = 0; i < numbers.length; i++) {
            that.total += numbers[i]
        }
        return that.total;
    };
    return that;
})();

document.writeln(object2.add(numbers) + " / " + valueOf(object2.add, numbers));
// outputs: 10 / 20
Scala: The Bad Parts?
 Does any of this have anything to do with Scala? Well, in the details, no, but in the general sense there is a very important approach in Crockford's book. His suggested solution to the problem above is simple: don't use 'this' in JavaScript and don't use its accompanying feature, 'new'. In fact, he suggests not using LOTS of features of JavaScript. He dedicates 14 pages of appendix to listing them.

Crockford also spends a very un-indulgent two pages discussing the modern phenomenon of "feature-driven design", both in products and programming languages, explaining how "Features that offer value to a minority of users impose a cost on all users" and concluding the book with this pearler: "It would be nice if products and programming languages were designed to have only good parts."
It was those two lines that caused me to reflect on Scala. I started asking myself questions like: Are there too many features in Scala? Are there parts that aren't "good parts"? Are there features that very few people make use of but that cost everyone because of the complexity they add? From my own experience, the answer to all three of these is "yes".

Crockford explains that we all crave simplicity and that, when we aren't given it, we make it for ourselves by creating our own feature subset, and I realised that I've done this to some extent in the way I use Scala. So finally I began wondering how long it will be until someone publishes "Scala: The Good Parts", complete with appendices listing all the features that you should steer clear of for everyone's sake. What confusing or dangerous feature of Scala do you reckon will be at the top of the list?

Want to learn more?

From Amazon...

From Book Depository...

3 comments:

  1. When I thinking of Scala, I'm not necessarily looking for a "Good Parts" book as much as a "Minimal" one like Tim Maher's Minimal Perl (http://minimalperl.com/). Perl is really bloated as a language, which I find kind of distasteful. Minimal Perl shows how to use a clean subset of Perl very effectively for what Perl does very well -- simple text processing one-liners on the command-line.

    In this sense, I'm really looking forward to the Manning book that some of the scalaz authors talked about doing that should help explain their attitude towards functional programming in Scala.

    The scalaz authors seem to have very strong architectural constraints with respect to how they use Scala (for instance, eschewing subtyping in favor of type class encoding with implicits). There are some Scala tutorials that subset Scala, but they normally do so in a trivial "Java++" way, which is very uncompelling for me.

    ReplyDelete
  2. DSL is bad, bad, bad.

    Here is a copy & paste from a very popular application:

    "* ^^"._? #> (a.!?(1, Get())) must be_==(Full(Answer(1))) ?~! eventually

    ReplyDelete
    Replies
    1. That's certainly hairy stuff! Although I wonder if the problem in your example is not the DSL per se but the use of operator overloading to implement a DSL. e.g. if the 4 operators ( _?, #>, !? and ?~! ) were instead words that described what the operation did, the DSL might be more readable?

      Delete