An Overview of CoffeeScript
Recently, I wrote about Dart, which is a new programming language for web development that compiles to JavaScript as an implementation detail. This article takes a look at CoffeeScript.
Like Dart, CoffeeScript compiles to JavaScript, but it's more than just an implementation detail. There is no plan to create a CoffeeScript VM; the language is designed as a preprocessor for providing some syntactic sugar that makes it easy to generate JavaScript code.
CoffeeScript, unlike Dart, encourages you to think about the JavaScript that will be generated. Reading the CoffeeScript reference puts me in mind of some early C references that made a point of discussing the assembly that would be generated from every language construct. This is useful because if you already know roughly how efficient the CoffeeScript is, you can quickly work out how efficient the JavaScript will be.
Code Density
One of the boasts of CoffeeScript is the density of the code that you can write. This is likely to set off alarm bells for anyone who has maintained a Perl codebase written by someone else—including a younger version of themselves. It's very easy to create a write-only language when this is your design goal.
One of the reasons why there is such a proliferation of programming languages is the large set of conflicting goals in language design. Languages, in the general case, are intended for communication. A programming language has even more difficult requirements, because it must be capable of communicating intent to humans reading it as well as compilers attempting to unambiguously interpret the semantics.
The more accurately the compiler can determine what it does, the better code it can generate. The easier it is for a human to read, the more maintainable the code. The more concise it is, the faster it is to write. Unfortunately, in most cases these are mutually exclusive goals. A gain in one area translates to a loss in one or more of the others.
Brevity is not necessarily the enemy of clarity, as some of the CoffeeScript examples show. List comprehensions are a good example. If you're familiar with any Lisp-influenced languages then you've probably seen them before. I mentioned them in my Haskell article, although the Haskell syntax—like much Haskell syntax—is far from being obvious.
List comprehensions run a small snippet of code on a list. JavaScript doesn't have lists, but it has arrays and objects, both of which are supported by CoffeeScript's list comprehensions. For example:
last = 0 last += num for num in [0..12]
This evaluates to an array containing the first 12 triangle numbers. There are two interesting feature here. The first is the [1..12] notation for quickly generating arrays of integers over ranges. This is very useful for constructing index sets.
The second is the list comprehension itself. The for num in part means that the expression before is evaluated with num set to each value in the array, in turn. If you use of instead of in then the comprehension will run over the key-value pairs in an object, like this:
alert ("#{key} => #{value}" for key, value of someObject)
This evaluates to an array of strings containing the key-value pairs in an object and then pops up an alert box containing it. This can be very useful for debugging.
Each of these examples in JavaScript would be a for loop, and the designers of CoffeeScript argue that this example is more concise. It's not entirely obvious that this is the case: They still have most of the structural elements of a for loop; the only difference is that they implicitly collect the result of each loop iteration into an array.
Perhaps a better example of brevity in CoffeeScript comes from the object and function shorthand. JavaScript already includes fairly concise syntax for creating objects—CoffeeScript just omits the braces and uses indentation to show scope—but the syntax for functions in JavaScript is quite verbose.
You can create a simple object representing a point like this:
point = x : 12 y : 12 angle : -> Math.atan(this.x/this.y) * 180 / Math.PI distanceFromOrigin: -> Math.sqrt(this.x*this.x + this.y*this.y)
This has two properties, the x and y coordinates, and two methods for returning the angle in degrees and distance from the origin. The -> notation is lists parameters on the left (none in these examples) and the body on the right.
This also shows a little bit more verbosity than some other languages. Note that the references to x and y need to explicitly reference them as this.x and this.y. In most object-oriented languages, instance variables are implicitly in scope for method bodies. CoffeeScript provides a halfway stage, by allowing this shorthand:
point = x : 3 y : 4 angle : -> Math.atan(@x/@y) * 180 / Math.PI distanceFromOrigin: -> Math.sqrt(@x*@x + @y*@y)
This is almost nice: It allows someone reading the code to know at a glance that x and y are properties of the object, not locals, arguments, or globals. Unfortunately, this also means that locals, arguments, and globals are syntactically indistinguishable. In a decent editor, variable scope will be indicated by color, so this extra syntax is largely superfluous.