Closure Mode for CoffeeScript

Unfancy Closure Library JavaScript
The JavaScript generated by CoffeeScript works perfectly fine, but it is not written in a way that can reap the full benefits of the Closure Compiler. The demo illustrates how an optional Closure Mode yields JavaScript that can be compiled effectively using the Advanced mode of the Closure Compiler. Supporting Closure Mode requires some small amendments to the CoffeeScript grammar, as discussed in the section on expressing dependencies.

How We Got Here

The Closure Tools were created to make it easier to maintain large JavaScript codebases, leveraging organizational constructs such as type-checking and namespaces. However, in creating the Closure Library, it was decided that it must always be possible to run Library code as ordinary JavaScript, i.e., without any sort of processing step. Therefore, it was not permissible for the Library to be written in any sort of superset/alternate form of JavaScript that was not recognized natively by browsers. The only viable option was to include any metadata needed by the Closure Tools inside JavaScript comments. This made it possible to build a rich set of tools, though it resulted in verbose source code due to all of the annotations.

Previously, most other attempts to address the JavaScript tooling issue were to use an existing programming language (that already had mature tools) and then create a tool that translated that existing language into JavaScript. This was the approach taken by the Google Web Toolkit (GWT) and ocamljs, among others. The common problem with that approach is that there is inevitably an "impedence mistmatch" between JavaScript and the language being translated into JavaScript. For example, Java supports ints and floats whereas JavaScript only has number. Further, JavaScript has first-order functions that are not available in Java, so it is not possible to use the full language when limiting oneself to GWT.

Recently, more developers have come to embrace JavaScript and there is more interest in the advancement of the language. As such, creating new languages (such as CoffeeScript) that are designed from the outset to be translated into JavaScript eliminates the "impedence mismatch" problem from before while making it possible to experiment with new language features. Google's Traceur project is specifically designed to make it possible to experiment with future features of JavaScript in the browser today.

Therefore, rather than awkwardly adding metadata in the form of comments in source code, a more elegant solution is to use a programming language with a grammar that makes it possible to express that metadata directly. For example, in Closure Library code, a constructor function is annotated with @constructor in a JSDoc comment, whereas in CoffeeScript, there is explicit support for classes via a class construct. This stronger grammar makes it easier to generate the JavaScript with annotations for the Closure Compiler, which is precisely the goal of Closure Mode for CoffeeScript.

Features of Closure Mode

This section enumerates the changes in JavaScript code generation when Closure Mode is used to generate JavaScript from CoffeeScript.

Creating a Class

A goog.provide() statement is automatically inserted for the class.
Normally, when writing Closure Library code by hand, the name of a class appears at least twice: once in a goog.provide() statement and again in the constructor function declaration. This results in repeated information that may get out of sync. Because a class is an official construct in CoffeeScript, the name of the class appears in only one place, so there is no possibility of inconsistency in the generated JavaScript.
JSDoc with a @constructor annotation is automatically inserted before the constructor function.
This ensures that if type-checking is enabled in the Closure Compiler, it will be able to recognize a constructor/class properly.

Creating a Subclass

JSDoc with an @extends annotation is automatically inserted before the constructor function.
This ensures that if type-checking is enabled in the Closure Compiler, it will be able to recognize a subclass relationship properly.
The goog.inherits() call required for subclassing is automatically inserted after the constructor function.
Normally, CoffeeScript needs to insert two functions into the generated JavaScript in order to establish the subclass relationship: __extends() and __hasProp(). After inserting these functions and declaring the constructor function, __extends() is called where goog.inherits() is normally called in Closure Library code. Therefore, instead of declaring any new functions, Closure Mode simply calls goog.inherits().
super calls in CoffeeScript are rewritten in a Library-appropriate way.
The __extends function in CoffeeScript introduces a property named __super__ whereas the goog.inherits function in the Closure Library introduces a functionally equivalent property named superClass_. Both of these properties are used in calling superclass constructors and methods, so they need to be updated in Closure Mode, accordingly.

Expressing Dependencies

An include construct is introduced into the CoffeeScript grammar that produces goog.require() statements.
In Closure Mode, the following line of CoffeeScript:
include goog.dom
becomes the following JavaScript:
goog.require('goog.dom');
It is more analogous to the import statement in Python. (include was chosen instead of import because import is a reserved word in JavaScript, so it already has some special handling in CoffeeScript.)
An include...as construct is introduced into the CoffeeScript grammar that produces goog.require() statements.
In Closure Mode, an include statement has an optional as clause that makes it possible to refer to the imported namespace via a shorthand. This is implemented such that it leverages goog.scope(), which is processed in a special way by the Closure Compiler in ScopedAliases.java. For example, the following line of CoffeeScript:
include goog.array as arr
Becomes the following Closure Library code:
goog.require('goog.array');

goog.scope(function() {
  var arr = goog.array;
;

}); // close goog.scope()
Therefore, throughout the rest of the file, wherever goog.array would be used, arr can be used as an alias instead.
goog.provide() and goog.require() statements are automatically sorted alphabetically in the generated JavaScript code.
This is not necessarily a big win, but it seemed like a nice touch!

Future Work

Although Closure Mode makes it possible to write Closure-Compiler-friendly JavaScript, it does not yet support all of the annotations in the Closure Library. This section lists the high-priority features that need to be added.

Type Information

For many, of the most compelling features of the Closure Compiler is its type-checker. Type-checking is only effective when the JavaScript being compiled is annotated with type information. There already appears to be some interesting work being done in this area, so perhaps the efforts of UberScript and Closure Mode can be combined.

Enums

In Closure, an enum is simply an object literal whose values are treated as though they were enum values. Because these values are frequently inlined by the Compiler and the enum definition is removed during compilation, it is illegal to iterate over an enums values using the for..in operator in JavaScript when code is compiled in Advanced mode. One alternative is to add a method to the enum that returns an array of the values:
/** @enum {string} */
TrafficLight = {
  RED: '#ff0000',
  YELLOW: '#ffff00',
  GREEN: '#00ff00'
};
TrafficLight.values = function() {
  return [TrafficLight.RED, TrafficLight.GREEN, TrafficLight.BLUE];
};
Unfortunately, this method is tedious to maintain by hand because it must be kept in sync with the actual enum values. If enum were a recognized construct like class in CoffeeScript, then both the declaration and the values() method could be generated so they were guaranteed to be in sync.

Utilities

Currently, Closure Mode focuses on converting classes effectively, but some Closure Library files are just collections of related utility functions (goog.array, goog.string, etc.). It should also be possible to create these types of libraries efficiently in CoffeeScript.

Leveraging More Closure Library Built-ins

By default, array iterators in CoffeeScript are translated into verbose for loops (though they are admittedly more efficient in terms of function calls):
# CoffeeScript
alert note for note in ['do', 're', 'mi']
roots = (Math.sqrt num for num in [1, 4, 9])
// Generated JavaScript
var note, num, roots, _i, _len, _ref;
_ref = ['do', 're', 'mi'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  note = _ref[_i];
  alert(note);
}
roots = (function() {
  var _j, _len2, _ref2, _results;
  _ref2 = [1, 4, 9];
  _results = [];
  for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) {
    num = _ref2[_j];
    _results.push(Math.sqrt(num));
  }
  return _results;
})();
In Closure Mode, the generated JavaScript would be more familiar to a Closure developer if it leveraged the existing goog.array.forEach() and goog.array.map() functions in the Closure Library:
goog.array.forEach(['do', 're', 'mi'], function(note) { alert(note); });
var roots = goog.array.map([1, 4, 9],
    function(num) { return Math.sqrt(num); });

plovr

plovr is a Closure build tool. One of its many features is that it makes the conversion of Closure Templates to JavaScript seamless. If there is sufficient interest, similar functionality will be added for CoffeeScript, using Rhino to run the CoffeeScript compiler.