My impressions after a week of CoffeeScript

Until a few days ago, I was a hardcore pure JavaScript believer.
I didn’t want to hear anything about Dart, TypeScript, LiveScript, CoffeeScript, IcedCoffeeScript and all the other compile-to-JS languages out there. To me, they were only an additional layer of stuff that could go wrong. I still think they’re all too focused on solving a non-issue with JS, namely the prototypal Object system. I absolutely love the approach used by Javascript when it comes to OOP because it takes the best functional-style features of JS and creates an elegant way of managing objects during runtime.

I’ve been following the development of ECMAScript 6 for many months, and I’m excited by all the new features, like list comprehensions, splats, array destructuring, syntactic sugar for map/reduce/filter/etc and many others. I’m sure you can see where I’m going with this. I knew CoffeeScript provided all that, but to me it was just a layer of features that are about to land in V8 any second now, so why bother learning it? Well, for one, the dense syntax is great. It can be really confusing at first. Take this snippet, for example. It prints the numbers from 1 to 5, waiting 1 second every time.

for (var i=1;i<=5;i++){
    (function(i){
        setTimeout(function(){
            console.log(i);
        }, i*1000);
    })(i);
}


If you’ve looked at CS before, you know that it can become tricky. The “do” keyword is nice to create an IIFE (Immediately-Invoked Function Expression), but passing a value to it can be curbersome if the name of the variables don’t match.

(function(i){
    //Do something with i
})(i);

easily becomes

do (i) ->
    //do something with i

But what if I want to do

(function(i){
    //Do something with i
})(annoying.and.complicated[variable].name[5]);

instead? The only difference is that I used a closure to simplify the name. The translation is very un-CoffeeScript with all those ugly parentheses [See twoolie’s comment at the bottom of this page for a better syntax]:

((i) ->
    //Do something with i
)(annoying.and.complicated[variable].name[5])

 

Another problem is when a function takes non-function arguments after a function argument. setTimeout is a good example:

setTimeout(function(){
    console.log("hi")
}, timeToWait);

Which one of the following is the correct syntax?

A
setTimeout ->
    console.log "hi", timeToWait

B
setTimeout (->
    console.log "hi")
    , timeToWait

C
setTimeout ->
    console.log "hi"
    , timeToWait

D
setTimeout ->
    console.log "hi"
, timeToWait

E
setTimeout( ->
    console.log "hi"
timeToWait)

F
setTimeout ->
    console.log "hi"
timeToWait

Only B, D and E compile to the right thing, but because your collegues and other people can use any variation of those, you need to know all the little gotchas.

Finally, the object literal syntax:

someFunction 500, abc : 123, def : 456

Does the above compile to this

someFunction(500, {"abc":123}, {"def":456});

or that?

someFunction(500, {"abc":123, "def":456});

The second one. CS is filled with gotchas and somewhat arbitrary design decisions like that. I know, they all kind of make sense in hindsight, but I feel like the CoffeeScript designers pushed the syntax minimalism a bit too far and readability is suffering from it. Of course, good programmers will use a consistent style and explicitly add brackets and parentheses to prevent confusing situations from happening. The problem is that most don’t. Despite those caveats, I expect CS to replace JS as my go-to language.

To conclude, here’s the CoffeeScript translation of the original example:
JS:

for (var i=1;i<=5;i++){
    (function(i){
        setTimeout(function(){
            console.log(i);
        }, i*1000);
    })(i);
}

CS:

for i in [1..5]
    do (i) ->
        setTimeout ->
            console.log i
        , i*1000

6 thoughts on “My impressions after a week of CoffeeScript

  1. Instead of

    ((i) ->
    //Do something with i
    )(annoying.and.complicated[variable].name[5])

    You can write

    do (i=annoying.and.complicated[variable].name[5]) ->
    //Do something with i

    Also, I always have these two functions in scope.

    after = (ms,cb) -> setTimeout(cb,ms)
    every = (ms,cb) -> setInterval(cb,ms)

    Which makes formulating your example trivial

    for i in [1..5]
    do (i) -> after i*1000, -> console.log i

    Finally, I think your gripes about object literal syntax are FUD. Nothing is forcing you to omit braces in object literals, it is merely a convenience. Further, idiomatic coffeescript would have you write the following to make it clear that the items are part of the same object.

    someFunction 500,
    abc : 123
    def : 456

    If you actually need them to be different objects, you write it like this.

    someFunction 500,
    abc: 123
    def: 456
    ,
    abc: 789
    def: 012

    The point is that EVERY language has multiple ways of writing the same thing. The trick is to make sure that you follow the established language idioms (or establish your own styleguide, and stick to it).

    • Thanks twoolie for the comments. I love the “do (i=something) ->” syntax and wasn’t aware of its existence.

      “The point is that EVERY language has multiple ways of writing the same thing. The trick is to make sure that you follow the established language idioms (or establish your own styleguide, and stick to it).”

      Yes, absolutely. The issue is that most programmers will develop their own style and stick to it, but when you end up working on a complicated project with a lot of different programmers the style can vary wildly within the project. The solution is to define and enforce a style, but in my opinion it would be nice if the language was a little bit more restrictive.

  2. Pingback: My impressions after a week of CoffeeScript | S...

Leave a Reply