Weblogs: Javascript

Full Frontal 2011: CoffeeScript Design Decisions by Jeremy Ashkenas

Tuesday, November 15, 2011

I'm skeptical about CoffeeScript. A large chunk of that skepticism is the idea that compiling one language into JavaScript is introducing additional layers of complexity that lead to maintainability issues, and difficulty in debugging problematic code. It's a cognitive dissonance that distracts from the task of developing production ready code.

Jeremy Ashkenas guided tour of CoffeeScript is informative and useful in seeing what's available. Essentially, CoffeeScript's principle is "it's just JavaScript". Except it isn't, it's syntactically different, and requires a compilation step to get it transformed to JavaScript.

It's just JavaScript

Ashkenas points out that the are plentiful cross-compilers for JavaScript, almost every computer language that has a modicum of current support. These languages are not JavaScript, and their paradigms are not directly related or relevant to JavaScript. CoffeeScript is different in that it embraces "JavaScript: The Good Parts", while ignoring the parts that don't make Crockford's list, and then cleans up the syntax; removing unwanted decoration/unnecessary boilerplate and adding in new syntactic sugar. Semantically CoffeeScript is JavaScript with a different syntax.

Essentially, the aim with CoffeeScript, and it seems that they are still on target, is the generated JavaScript is what you would have written in the first place (in a perfect world, at least). This means that debugging the code should be easier. This reinforces the guide that CoffeeScript isn't a first language, CoffeeScript users must know JavaScript, at least the Good Parts, and be comfortable with it.

So CoffeeScript isn't about avoiding JavaScript, or pretending it's Ruby. It's as JavaScripty as possible. It smoothes over the compatibility gaps (for example, Internet Explorer not having an indexOf method on Arrays, CoffeeScript polyfillers that in).

Throughout his talk Ashkenas points out the multiple applicability of CoffeeScript, not just for use in the browser, but also on the server with node.js, and also for scripting Photoshop.

History of CoffeeScript

CoffeeScript is almost two years old. It was first developed on Christmas Day 2009. Initially it was built in Ruby, using its lexer and parser libraries. At version 0.5 the CoffeeScript compiler was rewritten in CoffeeScript and since then has been self-hosting. Ashkenas takes great pride in knowing that an update to the CoffeeScript compiler is compiled with (the previous version of) CoffeeScript, and the resulting code (the new version of the compiler) then compiles the source code again. This self-hosing / bootstrapping process highlights how mature the CoffeeScript compiler already is.

Essentially CoffeeScript is written in CoffeeScript.

One idea with CoffeeScript is to use it as a basis of prototyping new JavaScript language features, so it's being tracked by various members (e.g. Brendan Eich) of JavaScript standards bodies. CoffeeScript's core features are a cleaner syntax, improved semantics, and a basis for new JavaScript goodies. In CoffeeScript everything is an expression. It also introduces list comprehensions to Arrays (so offering comprehension supporting array methods like filter, map and reduce)

JavaScript to CoffeeScript

To get from JavaScript to CoffeeScript involves the following steps:

So a JavaScript function:

var square = function(x) {
    return x*x;
};

In CoffeeScript becomes:

square = (x) -> x*x

Ashkenas takes the audience through the various JavaScript structures and shows how they look in CoffeeScript. What is surprising is the clarity of one-to-one mapping between the two languages. Object literals are already CoffeeScript ready: just remove the curly braces and commas (so no trailing comma problems), but keep the indentation. CoffeeScript objects are whitespace dependent (like Python).

Additional Features

CoffeeScript adds list comprehensions,

// Iterating through a list of values
list = ['Curly', 'Larry', 'Moe']
for stooge in list
    console.log 'Hi ' + stooge

// Iterating through a list of keys
for name of process
    console.log name

// Iterating through a range
for i in [0..10]
    console.log i

So a list comprehension in CoffeeScript looks like this:

list = [ 'Curly', 'Larry', 'Moe' ]
hello_stooges = for stooge in list when stooge isnt "Moe"
    "Hi " + stooge

console.log hello_stooges

In CoffeeScript everything is an expression, this means statements, and even try/catch blocks are expressions.

CoffeeScript adds splats (...), which is a way of dealing with variable number of arguments in JavaScript methods.

bestActor = (winner, others...)

CoffeeScript also introduces an existential operator (?), which allows a short circuiting of dot-operator variable references. When part of the dot-path isn't found, the expression returns undefined rather than the code collapsing with a "property not found", or null errors:

sue =
    name: "Sue Jones"
    age: 27

// Using the existential operator
console.log sue.non?.existent.property

// Existential operator can also be used on methods:
console.log sue.name.reverse?()

CoffeeScript's multi-line string blocks is similar to Pythons triple-quoted strings (with interpolation of variables done with #{varname} ):

haiku = """
    multi-line
    string
    block. #{varname}
"""

The multi-line features are available for comments (start and end them with three octothorpes), and more usefully in regular expressions (using three forward slashes), which certainly helps in the general readability of regular expressions.

Inheritance with CoffeeScript

Classes are simpler in CoffeeScript than JavaScript, though it looks more classical at face value than prototypical. This risks confusing newbies and leading to subtle bugs that will be hard to isolate. But, Ashkenas is clear, CoffeeScript is for developers who are already comfortable with JavaScript. Classical-looking inheritance is a case of the extends keyword, and the super() method call to invoke the parent class' behaviour:

class Beast
    speak: -> console.log "Roarrrr."

class Boid extends Beast
    speak: -> console.log "Tweet"

class Lion extends Beast
    speak: ->
        super()
        console.log 'The jungle sleeps tonight.'

new Beast().speak()
new Boid().speak()
new Lion().speak()

The -> is an indicator of a normally bound method (so the value of 'this' depends on how this method is called. CoffeeScript offers an alternative of a fat arrow ('=>') for the cases where the this reference is not what you expect, but you want this to be a reference to the class itself (so the fat arrow introduces the apply/call indirection to change what 'this' refers to in the method. Personally, the distinction between the normal arrow (->) and the fat arrow (=>) is crucial, and the visual difference in the code is too subtle that it can be easily overlooked when looking over code to isolate a particular bug. I'd prefer something more visually obvious to draw attention to the difference between a default scope and a corrected scope.

Hype-free CoffeeScript

Ashkenas avoids the ra-ra-ra CoffeeScript is great and everyone should use it. I actually found his realistic and practical evangelism a sign of confidence in CoffeeScript's abilities. CoffeeScript can sell itself without hype.

Though he does point out a very strong point about CoffeeScript; it's an excellent starting point for building your own JavaScript ; a platform for investigating and prototyping new features that can be added to the new version of JavaScript. It's a platform to nurture JavaScript's future. And that is compelling.

Current limitations

Though, at the moment, considering the lack of proper in-browser debugging tools which takes away the view-source friendliness of the Web, plus the essential need for developers to already comfortably understand scope, the workings of the this reference, and closures, CoffeeScript is not ready for adoption by mainstream web developers, and those working in large teams. But for a single, or very small team who know JavaScript well, this is perhaps an option. CoffeeScript reduces some of the boilerplate type situations of JavaScript (such as list comprehensions instead of boilerplate looping through arrays and functions), and a cleaner handling of inheritance (though the prototype is transparent, making CoffeeScript seem like a classical inheritance language), I wonder if CoffeeScript adds enough value to bridge the compile-and-run cycle, and currently murkifying debugging of code to justify its selection over plain old JavaScript.

I'm not completely convinced yet.


[ Weblog | Categories and feeds | 2011 | 2010 | 2009 | 2008 | 2007 | 2006 | 2005 | 2004 | 2003 | 2002 ]