Written by: steve ross on May 1st 2011

If you haven't seen or tried Coffeescript, and you use Javascript, it's certainly worth a look. If you are a Rubyist, you may not completely grok the way Coffeescript works at first. Or, maybe you will. I've been using Coffeescript for my last couple of projects which, admittedly, were not JS-intensive, but my confidence in it as a productivity enhancer is pretty darn high. I can go on about why it's cool, but plenty of other people have done that. Here's a few of my takeaways:

Coffeescript has classes. Oh, cool!

This is extremely useful in a couple of cases:

You want to augment something that's kind of already there, ala monkeypatching.

You can monkeypatch (i.e., modify behavior of existing classes) in Javascript, but that's a subject for a different day. A more common case is augmenting a standard facility by adding functionality. Here's an example. Say, you like the Javascript Math library, but you need a method to reduce the number of digits after the decimal. Not really the number of significant digits, but the digits of precision. I like to think of this as truncation:

class ExtMath extends Math
  @truncate = (x, precision = 0) ->
    scales = [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000]
    scale = scales[precision]
    Math.round(x * scale) / scale

This allows us to write code like:

console.log "3.4 meters = #{ExtMath.truncate metersToFeet(3.5), 2}"

A couple of points about the above. First, truncate() is what we think of in Ruby as a "class method". That is, it does not rely on the existence of an object of that type, nor does it rely on state of an instance of that type. This is often used in factory methods to create objects of a given type in much the same way as Rails' ActiveRecord.find does.

Second, and possibly as interesting is the fact that Coffeescript does string interpolation. Yes, it allows you to get away from using the + operator for putting together your formatted strings. Hash-curly-bracket inside double-quoted strings is interpolated, which is uber-cool.

Gotcha #1: Don't try interpolation inside of single quoted strings. Coffeescript only interpolates inside double-quoted strings.

Coffeescript has array comprehensions.

Prototype.js and jQuery both have iterators. Both are a bit difficult to grok, although Prototype's are more Ruby-like and as a result more natural to Ruby programmers. Coffeescript builds them in. The term "array" in array comprehensions is a bit misleading because arrays can be indexed by cardinality (i.e., a[1], a[1], ... a[n]) or as a hash (a['dogs'], a['cats'], a['giraffes']).

Comprehensions are ways of creating lists or a scalar from an existing list. The existing list is an array, as previously mentioned. So, say you have a list of last week's temperatures in Fahrenheit and need them in Celsius:

celsius = (fToC(num) for num in [63, 68, 72, 71, 74, 75, 76])

yields:

[ 17.22222222222222, 20, 22.22222222222222, 21.666666666666668, 23.333333333333332, 23.88888888888889, 24.444444444444443 ]

This is an example of mapping or transformation of an array to an array (a list to a list). A different problem is aggregating.

max_temperature = Math.max(max_temperature || 0, num) for num in [63, 68, 72, 71, 74, 75, 76]

This produces the expected (comfortable) 76 degrees Fahrenheit. Note that the syntax is:

left-hand-side = aggregation iterator

So you specify the iterator as a postfix expression.

Why does this create a scalar value instead of a list? Because you are assigning the result of the last expression evaluated, which happens to be Math.max(75 || 0, 76). In this case, max_temperature is being used as a memo variable.

One other wrinkle.

max_temperature || 0

simply says use max_temperature, but if I failed to initialize it, use 0. This is my C background showing. It could have as easily been written Math.max(max_temperature or 0, num).

Comprehensions across hashes

Sometimes it's useful to know the key as well as the value of the item you are operating on. So, for example, you might do:

hash = dog: 'woof' cat: 'meow' bird: 'tweet' bear: 'grrrrrr'

for animal, sound of hash console.log "a #{animal} goes #{sound}"

and the result is:

a dog goes woof
a cat goes meow
a bird goes tweet
a bear goes grrrrrr

More notes. First, I used console.log for output. Node.js implements this (as does Firefox and Safari, but sadly, not Internet Explorer). If you are running your script from the command line, Node will take care of the output for you. You can run it like this:

coffee myfile.coffee

Important and subtle information about key/value comprehensions. The in operator is replaced with the of operator. So you are not looking for a thing in an array, you are looking for a key/value pair of an object.

Well, more later.