An introduction to CoffeeScript
I first heard about CoffeeScript over a year ago, when we talked to Chris Williams about JsConf 2010, but it's really blown up lately. The official CoffeeScript website, CoffeeScript.org, sums it up beautifully:
CoffeeScript is a little language that compiles into JavaScript. Underneath all of those embarrassing braces and semicolons, JavaScript has always had a gorgeous object model at its heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way.
The golden rule of CoffeeScript is: "It's just JavaScript". The code compiles one-to-one into the equivalent JS, and there is no interpretation at runtime. You can use any existing JavaScript library seamlessly (and vice-versa). The compiled output is readable and pretty-printed, passes through JavaScript Lint without warnings, will work in every JavaScript implementation, and tends to run as fast or faster than the equivalent handwritten JavaScript.
We recently talked to Trevor Burnham on Herding Code about CoffeeScript - Trevor is the author of CoffeeScript: Accelerated JavaScript Development (Pragmatic Press), and I finally buckled down to take deeper look at CoffeeScript.
Why another language that compiles to JavaScript?
Let's step back for a minute...
Do you like Javascript [] Yes [] No
I love JavaScript... and yet I've developed a healthy fear of it. It's an amazingly deep language. Like many web developers, I followed this progression:
- I started by copying little scripts into my HTML to do simple things, and I thought it was a toy language. Heck, it's a scripting language, it's not rocket surgery, right?
- I used it more and more, and found that underneath some forced Java-esque syntax, the language was a good amount more complex than I thought. Somebody screwed up and complicated this scripting language!
- When I began to get an idea of the distinction between the DOM and Javascript (thanks in part to jQuery), I realized (and was taught) that Javascript is smarter than I am. It's a brilliant runtime hidden behind a strange Java syntax, the stigma of a "scripting" language, and poorly implemented browser DOM's. Thanks in part to JSMentors, I now know that I will really never "know Javascript". Recently someone asked me if I "knew JavaScript," and I responded by asking them if they "know computer programming."
Unlike some other JavaScript abstraction layers I've come across in the past, CoffeeScript isn't trying to generate code for you so you can avoid learning JavaScript. Rather, it's a better syntax for getting at and using the inner beauty of the JavaScript language. Trevor explained this well in the podcast - CoffeeScript doesn't "dumb down" JavaScript, it exposes the power of JavaScript in a more intuitive way - offering some syntax that probably should have been there all along.
For a better idea of what I'm talking about, see the following:
- Elijah Manor's MIX11 talk - Good JavaScript habits for C# developers
- JSMentors discussions
- http://wtfjs.com/ - lots of code samples which will surprise you if you expect JavaScript to work like C# or Java
Trevor's recent article in PragPub, A CoffeeScript Intervention, shows more information on some specific "quirks" in how JavaScript works, and shows how CoffeeScript improves the development experience.
It's Just JavaScript
One of the most important things to understand about CoffeeScript is that it really is just syntactic sugar over JavaScript. This is a really important point that came up several times during the podcast: CoffeeScript compiles down to JavaScript. This is powerful for several reasons:
Continue using JavaScript libraries and code (including jQuery)
You can continue using the JavaScript libraries, code snippets, API's, etc., that you're used to. That means you can call into JavaScript libraries like jQuery directly from CoffeeScript without any changes, as it all compiles right down to JavaScript anyways.
You can embed JavaScript in CoffeeScript
If you're converting JavaScript to CoffeeScript piecewise, or need to use snippets of JavaScript in CoffeeScript for some other reason, you can use the backtick to embed JavaScript directly in your CoffeeScript.
Less worry about technical debt
In software development, today's hot, exciting new library/language/pattern is often next year's problem. I've worked on a lot of projects which had invested in overhyped technologies in the past, burdening maintenance developers for years to come when the hype wore off, the original developers moved on to something else new and shiny (usually at another company), and more often than not the technology itself was discontinued or abandoned.
With CoffeeScript, that's not so much of a problem, as it's just JavaScript. At the very worst, if you decided that CoffeeScript wasn't working for you, you could just use the JavaScript output and go on with no harm done.
Clean generated JavaScript
As an industry, we've developed a healthy distrust of generated code. Eventually we'll need to look at it, and when we do we're horrified by how inefficient, ugly, and often just plain wrong the generated code is. Fortunately, CoffeeScript generates really clean JavaScript code. A few reasons:
- In most cases, it generates at a one-to-one level between CoffeeScript and JavaScript functional statements - not lines of code, but at the functional statement level.
- In the rare cases where CoffeeScript does introduce an opinion, the changes it makes correct some issues with JavaScript (variable scoping, encapsulation) that require a lot of code and attention to get right. For instance, creating a class with CoffeeScript will likely result in cleaner JavaScript code than if you wrote it yourself.
- In cases where you're converting from JavaScript, CoffeeScript is generally 2/3 as verbose as the original JavaScript and is generally more readable.
A few quick examples of CoffeeScript syntax
There's a lot to CoffeeScript - Trevor's written an entire book on it, and since the announcement that Rails 3.1 will support CoffeeScript, there's a good amount of blog content on it as well. I'm not going to try to cover the language here - there's just too much. Rather, I'll point out some of the features that caught my attention.
Significant Whitespace
I haven't ever really gotten excited about significant whitespace in server-side code, but I'm really seeing the benefit in JavaScript use. A great way to see this is to drop some legacy / random JavaScript into the js2coffee converter (note that this tool doesn't work in IE due to use of the const keyword) and see how it cleans up. I searched for some old, poorly formatted code as an example:
function luhnCheck() { var argv = luhnCheck.arguments; var argc = luhnCheck.arguments.length; var CardNumber = argc > 0 ? argv[0] : this.cardnumber; if (!isNum(CardNumber)) { return false; } var no_digit = CardNumber.length; var oddoeven = no_digit & 1; var sum = 0; for (var count = 0; count < no_digit; count++) { var digit = parseInt(CardNumber.charAt(count)); if (!((count & 1) ^ oddoeven)) { digit *= 2; if (digit > 9) digit -= 9; } sum += digit; } return (sum % 10 == 0) }
Without any other changes other than a simple automated conversion, here's that same function:
luhnCheck = -> argv = luhnCheck.arguments argc = luhnCheck.arguments.length CardNumber = (if argc > 0 then argv[0] else @cardnumber) return false unless isNum(CardNumber) no_digit = CardNumber.length oddoeven = no_digit & 1 sum = 0 count = 0 while count < no_digit digit = parseInt(CardNumber.charAt(count)) unless ((count & 1) ^ oddoeven) digit *= 2 digit -= 9 if digit > 9 sum += digit count++ sum % 10 == 0
Keep in mind that this is an automated conversion that's not taking advantage of a lot of CoffeeScript's advanced features, but already that code is a lot more readable. Significant whitespace mandates indentation of code blocks, which makes it easy to see the code structure, and since significant whitespace makes all the braces and semicolons (and many of the parentheses) unnecessary, it's less code that's easier to read as well.
There's a great RailsCast on CoffeeScript which shows the conversion of a class to CoffeeScript (11 minutes long). While it covers a lot of topics, the advantages of significant whitespace are a key thing I took away.
Classes
JavaScript doesn't have a class syntax, since it's a class-free, prototypal language. There are a variety of techniques for building classes in JavaScript, which are often verbose and/or wrong in subtle ways. CoffeeScript takes advantage of the fact that "class" is a reserved but unused word in JavaScript, allowing making it very easy to write classes with terse, readable code that implement a class pattern with inheritance. Here's an example from the Coffeescript site:
class Animal constructor: (@name) -> move: (meters) -> alert @name + " moved " + meters + "m." class Snake extends Animal move: -> alert "Slithering..." super 5 class Horse extends Animal move: -> alert "Galloping..." super 45 sam = new Snake "Sammy the Python" tom = new Horse "Tommy the Palomino"
A few things to notice / remember here:
- This is really just syntactic sugar which makes it easier to implement a class pattern in JavaScript. It's not actually adding full class support, but it is allowing you to take advantage of object orientated design when you'd like to.
- CoffeeScript's generated class code uses a self-executing anonymous function to protect against namespace violations. This is a common pattern that works pretty well, but is a bit verbose if you're handwriting the JavaScript. You can see more about that in Elijah Manor's MIX11 talk, Good JavaScript Habits for C# Developers (specifically the bit starting at 35:00).
- One potential downside of this pattern is that (from what I've read) you don't get true encapsulation / private variables. If you really want that, you can implement your own class pattern in CoffeeScript, but I think the prevailing wisdom is that unenforced encapsulation is fine in most cases.
I recommend Justin Reidy's post on Classes in CoffeeScript for more info on how classes are being implemented.
Functions
Writing functions in JavaScript always feels a bit clunky to me, especially since anonymous functions are so common in today's JavaScript. One place where it's very obvious is the }); (affectionately known as the "close Resig") that gets repeated so often in jQuery I've expected it to start showing up as a bonus button on programming keyboards.
Functions in CoffeeScript eliminate a lot of (oh dear, this word has always bugged me) ceremony in function declaration. Here are some ways it does that:
- Functions are declared using the arrow (->) operator
- Implicit returns (i.e. the value of the final statement in the function is automatically returned as the output of the function) are especially useful for short and anonymous functions
- The use of significant whitespace means that you get rid of all the ();}); goo
Here's an example from the CoffeeScript documentation which shows how JavaScript functions are dramatically simplified in CoffeeScript. Here's the original JavaScript:
var cube, square; square = function(x) { return x * x; }; cube = function(x) { return square(x) * x; };
And here's that same code expressed in CoffeeScript:
square = (x) -> x * x cube = (x) -> square(x) * x
For more on Functions in CoffeeScript, see Jan Van Ryswyck's post Exploring CoffeeScript Part 2 – Variables and Functions.
Variables
Jan's post also points out some great features in CoffeeScript with regard to Variables. Variables in JavaScript are a bit more tricky than you'd expect - it's a little to easy to declare something in global scope or get twisted around by variable declaration hoisting. I'll assume you're familiar with scoping - most developers know about it, but still make mistakes out of laziness. Hoisting is lesser known, and can get you in trouble.
Jeffery Way sums up variable hoisting like this:
Within its current scope, regardless of where a variable is declared, it will be, behind the scenes, hoisted to the top. However, only the
declaration
will be hoisted. If the variable is alsoinitialized
, the current value, at the top of the scope, will initially be set toundefined
.
Elijah's talk goes into it in a lot more detail, but the point is that if you declare and initialize a variable halfway through a function, it will actually be declared at the beginning of the function. Since functions are variables in JavaScript as well, functions are also affected by hoisting.
Improper scoping (declaring a JavaScript variable without using var) can put your variable in global scope, meaning it's hoisted above the function that declared it. By ensuring proper scoping of your variables, CoffeeScript takes care of some tricky hoisting issues as well. Declaring a variable is simplified because you don't have to declare variables - just use them and CoffeeScript takes care of declaring them locally.
Next Steps
If I've succeeded in convincing you to find out more about CoffeeScript, here are some easy ways to get started:
- Listen to our Herding Code interview: Herding Code 114: Trevor Burnham on CoffeeScript
- Watch the 11 minute RailsCast: CoffeeScript Basics. Don't worry if you don't know Ruby or Rails - it just covers JavaScript to CoffeeScript conversion. You can read the transcript from the RailsCast here.
- Convert some existing JavaScript to CoffeeScript using the js2coffee page (remember, doesn't work on IE)
- Buy Trevor's book CoffeeScript: Accelerated JavaScript Development, available in ebook format
- View the docs at CoffeeScript.org - there are plenty of well explained code samples.
- Play with the Try CoffeeScript interactive window at CoffeeScript.org. This is a fantastic way to kick the tires - just type in some code and see it converted as you type. Browse to CoffeeScript.org and click Try CoffeeScript to get started.