Peter Lyons

Mar 05, 2012

CoffeeScript and Progress

If you like CoffeeScript, you should write applications in CoffeeScript. If you prefer JavaScript, you should write applications in JavaScript. In this post I want to address the common attitude I have found among JavaScript programmers that CoffeeScript is a priori somehow wrong or inferior or a bad choice. I'll briefly recount some of the technical reasons why I like and use CoffeeScript at the end of this post, but the focus is a rejection of the attitude above on philosophical grounds. To be clear, the position I'm taking issue with is the position that JavaScript is the one and only language we should be writing in for the browser and node.js.

Philosophical Rejection of Monoglotism

I brought up the topic of Google's Dart language at a local tech meetup when Dart was first announced. I asked people what their reaction was. The few who responded seemed to dislike it, not for any particular technical objection to the language or its features, but simply to its existence at all, which surprised me. This struck me as completely unexpected and strange. Why, I asked. "JavaScript is a standard" came back from one person. "Why do you like it?", he asked. I said I didn't yet know much of anything about it, but I liked the creation of new programming languages for the browser as a basic idea. I said that choice of programming tools is a good thing, and JavaScript is clearly flawed in extremely well-understood ways. Why is there this relentless stranglehold on universal and eternal backward compatibility? What is the cost/benefit analysis that goes into that?

Software in general, including programming languages and tools, must continually advance, and the faster, the better. Addressing backward compatibility by slowing forward progress is a losing solution. Ye olde for loop, while venerable, has been surpassed. Let it go. I agree with Bruce Eckel here in that once you have a stable version of a language, authors who "don't want to deal with those changes" should be able to just continue using that version while the current version of the language advances. If you wrote a JDK 1.2 application in 2000 or so and you still want to run it, just keep running it on JDK 1.2. Don't hold the entire java language back for the lifetime of this application which you are unwilling to maintain and advance along with the platform. Browsers should do the same thing with their javascript engines every 2 years or so. If you want to write a codebase once and not update it, ship it on custom hardware that does not include networking capability and call it a day. If you want to write software that works on the Internet, commit to a certain amount of ongoing maintenance. Even if we had perfect technical backward compatible support, the state of everything else: UX, UI, performance, data formats, protocols, continues to advance. Craigslist.org can get away with keeping the same UI for a decade because they make their own rules. Every other site needs to modernize.

I overheard a well-regarded server-side JavaScript programmer complain about CoffeeScript saying something along the lines of "Why would you write a module in some language I don't know?". WTF? How would it sound if I said that about any other server side programming language? It would sound ridiculous, because it is.

This notion that the web is ever going to be some all-purpose panacea built upon a single markup language plus a single stylesheet language plus a single programming language is a false goal. Let it go. Embrace a variety of tools. The vast majority of web sites are not built from direct HTML files. HTML is generated by dozens of highly divergent programming languages across many paradigms with all combinations of trade-offs. So it should be for the application in the browser. It's not a problem on the server, and it's not going to be a problem in the browser.

Rejection of FUD

CoffeeScript is clearly a well-thought-through and beautiful language. It brilliantly removes the terrible parts of javascript and replaces verbose javascript boilerplate with elegant expressiveness. For example, the function keyword being replaced with the gorgeous arrow symbol -> and the introduction of list comprehensions. It is so small and concise that there are no glaring warts to be ironed out. If you like python or ruby, you will probably like CoffeeScript better than JavaScript. If you try it for 6 months and change your mind, compile it to javascript and use that as your no codebase. No harm, no foul.

But but but...

Debugging. I hear arguments that because the line numbers in the browser stack traces don't align with the line numbers in your .coffee files, debugging is harder in CoffeeScript than JavaScript. I don't find this a problem. I just don't. My variable and function names are preserved. I anchor to a function name and my functions don't get exceedingly long. I see that in the browser the error is a few lines down in the "loadUserList" function and I go look at the "loadUserList" function in CoffeeScript and find the problem without a lot of fuss. In my experience there's just no impact here. I don't really know why people bring this up so often. It's just a non-problem for me. We use multiple preprocessing techniques including concatenating JS files together. Line numbers just ain't never gonna map back to actual files in your editor. Get over it.

In case you didn't know

Here's a sample BDD spec in CoffeeScript from a gist on github.

describe 'Math:', ->
  describe 'fib()', ->
    it 'should calculate the numbers correctly up to fib(16)', ->
      fib = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
      expect(Math.fib(i)).toEqual fib[i] for i in [0..16]

  describe 'uuid()', ->
    it 'should have the proper UUID format', ->
      expect(Math.uuid()).toMatch /[A-Z0-9]{8}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{13}/

    it 'should have always the numer 4 at position 14', ->
      expect(Math.uuid()).toMatch /[A-Z0-9]{8}-[A-Z0-9]{4}-4[A-Z0-9]{3}-[A-Z0-9]{4}-[A-Z0-9]{13}/
      expect(Math.uuid()).toMatch /[A-Z0-9]{8}-[A-Z0-9]{4}-4[A-Z0-9]{3}-[A-Z0-9]{4}-[A-Z0-9]{13}/
      expect(Math.uuid()).toMatch /[A-Z0-9]{8}-[A-Z0-9]{4}-4[A-Z0-9]{3}-[A-Z0-9]{4}-[A-Z0-9]{13}/

    it 'should generate a unique uuid for 1000 generated uuids at least', ->
      uuids = []
      counter = 0

      while counter < 1000
        uuids.push Math.uuid()
        counter++

      expect(uuids.length).toEqual _.uniq(uuids).length

And here's a similar jasmine spec in JavaScript.

describe("NotesView", function() {
  beforeEach(function() {
    var notes = new NotesCollection();
    spyOn(notes, "fetch");

    var view = new NotesView({collection: notes});
  });

  describe("#initialize", function() {
    it("should fetch the notes", function() {
      expect(notes.fetch).toHaveBeenCalled();
    });

    describe("on notes fetch success", function() {
      beforeEach(function() {
        var request = mostRecentAjaxRequest();
        request.response({
          status: 200,
          responseText: JSON.stringify([
            {body: "Blog post #1", id: "1"}
          ])
        });
      });

      it("should render the view", function() {
        expect($(view.el).find(".post")).toHaveText("Blog post #1");
      });
    });
  });
});

Ouch, my eyes! All those closing punctuation groups!

Reduction of boilerplate and increasing expressiveness are what I want from a language. CoffeeScript delivers strongly in these areas. It compiles to JavaScript and works right now in all browsers and in node.js.

Conclusion

It's a polyglot world. Every app is going to use more than one programming language. Work toward making the programming languages we use better instead of trying to shame us into continuing to use languages as better alternatives are created. Shame on you. Move forward.