Follow us on RSS or Twitter for the latest updates.

June 10, 2011

CoffeeScript: The beautiful way to write JavaScript


coffee script javascriptI have coded a lot of years in JavaScript and I have produced a lot of JavaScript code. Yet even with all my experience I still struggle to make JavaScript beautiful. In this post I'll explore why writing beautiful JavaScript is hard and how to improve this with CoffeeScript - a minimalistic language that compiles to JavaScript.

What is beautiful code?


Given that beauty is subjective I want to state how I define beautiful code:
  • beautiful code uses the least amount of code to solve a given problem
  • beautiful code is readable and understandable
  • beautiful code is achieved not when there is nothing more to add, but when there is nothing left to take away (just like great designs)
  • the minimal length is a side-effect of beautiful code and not a goal or a measure
So for me beautiful code is a synergy between minimal code, functional code and readable code.

An example of beautiful JavaScript code

Let's take an example with the Fibonacci function as it's a function that most programmers should know.

Here is an implementation that I don't find beautiful, because the code has little structure and uses a lot more code than necessary:
function f(n) {
var s= 0;
if(n == 0) return(s);
if(n == 1) {
s += 1;
return(s);
}
else {
return(f(n - 1) + f(n - 2));
}
}
Here is another implementation that I find more elegant and beautiful, especially if you are familiar with the one line if else:
function fib(n) {
return n<2 ? n : fib(n-1) + fib(n-2)
}
I find this implementation beautiful as well, i.e. the number of lines don't matter that much:
function fib(n) {
if (n < 2)
return n
return fib(n-2) + fib(n-1)
}
The problems with JavaScript

I think one of the main problems with JavaScript is that it's a confusing hybrid between many different programming paradigms:
  • JavaScript is a functional language
  • JavaScript is an object oriented language, but it's prototype based
  • JavaScript is very dynamic and is a lot closer to Lisp than C/Java, but it has a C/Java syntax
  • Even JavaScript's name is confusing as JavaScript has very little to do with Java
In the end we have a language that has an identity crisis and we have programmers that try to force their paradigm onto JavaScript. And forcing a paradigm onto JavaScript isn't that good, because JavaScript isn't Java, it isn't Scheme and it isn't Python. JavaScript is JavaScript and it has some strengths and some weaknesses like all other programming languages.

JavaScript was also designed in a haste and some bad decisions reflect this - - like the dynamically scoping of this or the syntax used for inheritance. This is a big deal since fixing these problems is very hard due to backward compatibility issues. Here is a great quote from the creator of JavaScript that highlights the environment that JavaScript was born in:
JavaScript had to look like Java only less so, be Java’s dumb kid brother or boy-hostage sidekick. Plus, I had to be done in ten days or something worse than JavaScript would have happened.

— Brendan Eich

CoffeeScript: The new way to write better JavaScript

coffee scriptCoffeeScript is a minimal language that compiles to JavaScript. It aims to expose the good parts of JavaScript in a simple way. Before we look deeper into CoffeeScript I want to share my story of how I ended up with CoffeeScript.

I stumbled upon CoffeeScript a while ago and I tried to write a few scripts to get a feel of the language. I really loved coding in CoffeeScript, but the language itself was very immature when I tried it. Now fast forward to last week I had a serious issue with the JavaScript code I have written for Wedoist. I looked at it and said to myself this is garbage, there must be a better way to write this. I also looked at Wedoist's Python code and it seemed clean, minimal and readable. I then researched a lot on how to write better JavaScript code. My thoughts process was something along this line:

* Maybe I have missed some JavaScript idioms?
* Let me check how other open-source projects write their JavaScript, maybe they have figured a better way?
* Maybe I should try using backbone.js?
* Maybe I should read JavaScript: The Good Parts (again)?

I was blocked. As a way to explore options I took a small part of Wedoist's JavaScript code and tried to improve it. I tried my best to refactor and rewrite into some beautiful code I could be proud of... But no matter how hard I tried I could not make my JavaScript code as clean as my Python code. I was frustrated, especially since I code so much in JavaScript.

Here is where CoffeeScript enters the picture: I got the idea to rewrite to CoffeeScript, because maybe it's not my approach that's holding me back, but JavaScript itself. So after some hacking I rewrote the small part into CoffeeScript... and ... I was delighted! My CoffeeScript part looked so readable, so minimal and ultimately beautiful. I have found a way to write beautiful "JavaScript" by writing in another language that compiles to JavaScript.

CoffeeScript does not deprecate your JavaScript code

One of the things I love about CoffeeScript is that it compiles to JavaScript. This means I can reuse all of my current JavaScript code - I don't have to rewrite anything to CoffeeScript. This is a great deal, especially since Wedoist JavaScript codebase is quite large. It would suck to spend months rewriting to another language.

CoffeeScript also mimics JavaScript. It feels like a much improved version of JavaScript where the bad parts are removed or replaced. It also moves JavaScript away from the C/Java syntax and into the syntax of Ruby or Python (which is great, because JavaScript is a lot closer to Ruby or Python than it is to C or Java).

I have now a structure where I can just write new parts of my code in CoffeeScript and I plan to rewrite old parts into CoffeeScript as I move along. It's a great way to move a system forward as big rewrites are one of the single worst strategic mistake that any software company can make.

How CoffeeScript gets compiled to JavaScript

To clarify compilation let's take an example and see how it works.

CoffeeScript code:
square = (x) -> x * x
cube = (x) -> square(x) * x
"Compiled" JavaScript code:
var cube, square;
square = function(x) {
return x * x;
};
cube = function(x) {
return square(x) * x;
};
As you can see from the above example the mapping between CoffeeScript and JavaScript is pretty straightforward. On their website you can find a lot more examples of how CoffeeScript gets compiled to JavaScript.

CoffeeScript: Rewrite example

To give you a feeling of CoffeeScript here is a small JavaScript example I have rewritten to CoffeeScript:
get: function(offset, callback, limit) {
var self = this;

var data = {
project_id: Projects.getCurrent().id,
limit: limit || this.default_limit
}

if(offset)
data.offset = Calendar.jsonFormat(offset, true);

this.ajax.getArchived(data, function(data) {
if(!offset)
self.setCache(data);
callback(data);
});
},
The CoffeeScript code looks like this:
get: (offset, callback, limit) =>
data =
project_id: Projects.getCurrent().id
limit: limit or @default_limit

if offset
data.offset = Calendar.jsonFormat(offset, true)

@ajax.getArchived(data, (data) =>
if !offset
@setCache(data)
callback(data)
)
As you can see they look very similar, but in my opinion CoffeeScript looks a lot more crisp because all the unnecessary syntax is removed and we have only the essentials left.

Now let's take a look of some highlights of CoffeeScript.

CoffeeScript highlight: Inheritance made easy

JavaScript has a great inheritance system, but the syntax for it is horrific. CoffeeScript fixes this with an elegant inheritance system that mimics how classes and inheritance work in most other languages:
class Animal
constructor: (@name) ->

move: (meters) ->
alert @name + " moved " + meters + "m."

class Snake extends Animal
move: ->
alert "Slithering..."
super 5
CoffeeScript highlight: Arrays on steroids

I love List Comprehensions in Python and CoffeeScript has them!
list = [1, 2, 3, 4, 5]
cubes = (math.cube num for num in list)
With array slicing:
copy = list[0...list.length]
Array generators:
countdown = (num for num in [10..1])
CoffeScript highlight: Strings on steroids

CoffeeScript borrow's Ruby's syntax for string interpolation, which makes it easier to construct strings:
author = "Wittgenstein"
quote = "A picture is a fact. -- #{ author }"
Multi-line strings are allowed:
mobyDick = "Call me Ishmael. Some years ago - never mind how long precisely -- having little or no money in my purse, and nothing particular..."
CoffeeScript highlight: Binding this

this keyword in JavaScript is partially broken because it's dynamically scoped. CoffeeScript fixes this if you use => keyword (it will automatically bind this or @ for you):
Account = (customer, cart) ->
@customer = customer
@cart = cart

$('.shopping_cart').bind('click', (event) =>
@customer.purchase @cart
)
Explore CoffeeScript!

I have only scratched the surface of what CoffeeScript has to offer. Please check out their site for more details and their awesome annotated source code which is written in CoffeeScript (yes, CoffeeScript is written in itself!)

I am still exploring CoffeeScript and so far it's one of my favorite languages. A big kudos goes to Jeremy Ashkenas for writing it.

As always happy hacking, I hope you give CoffeeScript a try and don't forget to subscribe in order to receive more programming tips direct to your inbox.