Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Why I'm (Finally) Switching to CoffeeScript (codelikebozo.com)
66 points by darkxanthos on Sept 5, 2011 | hide | past | favorite | 44 comments


Some nice examples, if a bit unfair since the JS is clearly compiled from the CoffeeScript rather than hand-coded. In fact, the last example reveals a bug in CoffeeScript—the function

    (x, y) ->
      for game_piece in @game_pieces
        if game_piece.is_at x, y
          @held_item = game_piece
          @held_item.start_dragging x, y
          break
compiles to

    function(x, y) {
        var game_piece, _i, _len, _ref, _results;
        _ref = this.game_pieces;
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          game_piece = _ref[_i];
          if (game_piece.is_at(x, y)) {
            this.held_item = game_piece;
            this.held_item.start_dragging(x, y);
            break;
          }
        }
        return _results;
      }
Notice that _results is initialized as an empty array... and is never modified, making it an utterly pointless return value. What's happening is that the CoffeeScript compiler has both implicit returns and list comprehensions, which means that it tries to return an array of all the values in the loop. All well and good, but the compiler doesn't try to do a list comprehension when there's a break... and yet it creates the frivolous _results variable anyway.

Obviously I'm a fan of CoffeeScript; this is a minor bug and I expect it to be fixed. But let's be fair in making the case for it. Well-written JavaScript will always look better than CoffeeScript output; it just takes a lot more work.


The proper way to avoid the return _results is just put a blank return statement in the last line of the function.

    (x, y) ->
      for game_piece in @game_pieces
        if game_piece.is_at x, y
          @held_item = game_piece
          @held_item.start_dragging x, y
          break
      return


To be fair, lack of optimization by a compiler is not a bug--unless it claims to do that kind of optimization.


Well, there was a related inconsistency which was certainly a bug. And it's now fixed: https://github.com/jashkenas/coffee-script/issues/1669


When you directly compare coffeescript to JS and looking at more or less the same code (but of course it's very unfair to JS to use coffee compiler output instead of hand written JS), you won't find much of a difference aside of the small syntax changes.

But once you are used to coffee, your code will start to look very different from pure JS code. Coffe encourages you to write even more, even smaller functions due to its lean syntax.

This new coding style that emerges (at least it began to emerge for me and a coworker) is infinitely more readable than JS, even more so when you look at a direct translation.

My takeaway from this is two things:

1) coffee is cool. Just give yourself one time to adapt. At first everyone's code in $NEWLANGUAGE looks like code in $OLDLANGUAGE until you get used to the common idioms in your new language.

2) The general syntax of a language can have a huge effect on your code style which in turn can have a huge effect on overall readability.


I haven't seen that yet but I can imagine it happening. I love Javascript but coding lambdas always felt ugly enough that I used it more in C# than JS. It is definitely true CS fixes that for me.


At least for the first example, the reason the JavaScript code is so bad is that the CoffeeScript code is equally bad. It repeats an entire for loop for each character code!

Having a concise syntax doesn't relieve you of the duty to simplify your code. Both the CoffeeScript and JavaScript code can be simplified considerably, as several others have pointed out.

In fact, if you clean up the CoffeeScript code like this:

    keyMoves =
        37: 'pan_left'
        38: 'pan_up'
        39: 'pan_right'
        40: 'pan_down'
        187: 'zoom_in'
        189: 'zoom_out'

    document.onkeydown = ( event ) ->
        move = keyMoves[event.keyCode]
        piece[move]() for piece in game_pieces
        the_screen.refresh( game_pieces )
It compiles to this JavaScript code:

     var keyMoves;
     keyMoves = {
          37: 'pan_left',
          38: 'pan_up',
          39: 'pan_right',
          40: 'pan_down',
          187: 'zoom_in',
          189: 'zoom_out'
     };
     document.onkeydown = function(event) {
          var move, piece, _i, _len;
          move = keyMoves[event.keyCode];
          for (_i = 0, _len = game_pieces.length; _i < _len; _i++) {
               piece = game_pieces[_i];
               piece[move]();
          }
          return the_screen.refresh(game_pieces);
     };
And that's a quite reasonable piece of code - nothing like the JavaScript example in the article.

Personally I would write the JavaScript version more like this (assuming I wanted a raw for loop instead of an iterator function, perhaps for speed):

    keyMoves = {
        37: 'pan_left',
        38: 'pan_up',
        39: 'pan_right',
        40: 'pan_down',
        187: 'zoom_in',
        189: 'zoom_out'
    };

    document.onkeydown = function( event ) {
        var move = keyMoves[event.keyCode];
        for( piece, i = -1;  piece = game_pieces[++i]; )
            piece[move]();
        the_screen.refresh( game_pieces );
    };
But I certainly don't see anything very wrong with the CoffeeScript-generated JavaScript, once the original CoffeeScript code is improved.


I find CoffeeScript pushes programming in the browser over the crucial tipping point between Not Fun and Fun. (I'm almost embarrassed by how much of a sucker I am for syntax, but there it is).


> I'm almost embarrassed by how much of a sucker I am for syntax, but there it is

It's interesting how so many people profess love for CF's syntax, where I have such a dislike for it (to me, it looks like a magpie language: rather than a whole design it's cobbled from shiny pieces taken from all over the place; the way some features are "integrated" are close enough to yet far enough from the source language that it lands straight in my uncanny valley; it's drawing back from the OO/Func movement of the JS community back into procedural and statements-based something fierce; the target language leaks heavily through such things as the thin v fat arrows; and finally it draws back from things I think are genuinely better features of javascript, such as explicit scopes)


> it's drawing back from the OO/Func movement of the JS community back into procedural and statements-based something fierce

This statement is completely incorrect, the truth is actually the exact opposite.

First I would note there is no such thing as a statement in Coffeescript. Everything is an expression ... this may seem like a minor point to some, but it's actually quite a powerful difference. As with a lot of the things CS adds, It's hard to appreciate the boost in expressiveness until you start writing a lot of code with it.

If you want to talk about functional programming, expressions rather than statments count as 1 whole point out of 9 for PG's "list of things that are awesome about lisp".

Another important aspect of functional programming is lambdas, JavaScript does not have "real lambdas" because of the lack of implicit returns and the aforementioned quality.

By adding them, CS get's you much closer to real lambdas. This combined with the light syntax for functions encourages one to program in a much more functional style.

These qualities enable say, quick and easy decorators:

    logged = ( f , named_f ) ->
        if named_f? then [ f, name ]  = [ named_f, f ] else name = "function"
        # decorated function, to be returned
        ( args... ) ->
            console.log "#{name} (#{f.length}) with #{args.length} args."
            f.apply @, args
    
    # random function, note our decorator preserces "this"
    foo = logged ( x ) ->
        console.log( @[x] + "" )

    # random function that calls other function, our logging lets us trace the calls
    # this one provides a name
    bazz = logged "bazz", ( x ) ->
        foo.call @, x
    
    # test
    bar =  fizz: "buzz"
    bazz.call bar, "fizz"
Note also how splats simplifies this operation. Destructuring and the existential operator makes the logic in the decorator function a little cleaner. String interpolation makes the logging easier.

Additionally the use of implicitness really makes it shine and fit together well, rather than being mere eye-candy. There's no wrapping in parens to decorate, making it harder to mess up, easier to modify, and easier to parse visually and perhaps programatically.

I think a lot of people are deceived when it comes to CoffeeScript, they see a lot of little things which seem nice...but individually they seem pointless or minor. They are deceived on two parts: a lot of these "little things" are actually quite powerful on their own, and additionally they mesh in a way that is greater than the sum of the parts.

All these little nice things can add a powerful boost to expressiveness and once you spend some time with them it really changes how you think and write code. That's another thing I would note is that CoffeeScript is for people who want more expressiveness/find these things more expressive. If you are looking for something else, or think in a way that has an impedance mismatch with the CoffeeScript philosophy, obviously you should look elsewhere.

(If anyone's curious, this decorator makes it so every time a decorated function is called, it logs the function's name if provided, the number of defined arguments it takes, and how many it was actually passed.)

I would also note that scopes function the same in CS as they do in JS, the only difference is that in CS you cannot shadow variables from an enclosing scope, and there are no implicit globals.

In terms of OO, CS adds syntactic sugar for perhaps the most common inheritance pattern in JS.

I would argue both of those are good things, though I respect other people might think otherwise.


> var game_piece, _i, _j, _k, _l, _len, _len2, _len3, _len4, _len5, _len6, _m, _n;

I dont see why he didn't use do "var game_piece , _i , len;" He doesn't need multiple i's nor lengths. The JS example is just a very poor example of coding. (in fact he could of just simplified it further down by having one loop and a variable to the method, to be executed on those pieces.)

CoffeeScript is very nice, but I want to see examples of why to use it that will actually save me time. Compared to the examples, there are better ways to code and tons of easy ways to do OO with certain frameworks/toolkits.


    var keydown_action_map = {
      37: 'pan_left',
      38: 'pan_up',
      39: 'pan_right',
      40: 'pan_down',
      189: 'zoom_out',
      187: 'zoom_in'
    }
    document.addEvent('keydown', function(event) {
      var action = keydown_action_map[event.keyCode];
      game_pieces.each(game_piece[action], game_piece);
    });


This can be written like this too:

  keydown_action_map = 
        37: 'pan_left'
        38: 'pan_up'
        39: 'pan_right'
        40: 'pan_down'
        189: 'zoom_out'
        187: 'zoom_in'

  document.addEvent 'keydown', (event) ->
    action = keydown_action_map[event.keyCode]
    game_pieces.each game_piece[action], game_piece
If you can write terse code in JS you can write it even more terse way in CS.


Where "more terse" means "removing a useful keyword and replacing parens by spaces".

Not exactly impressive (and coffeescript's symbolless maps read horribly badly, imo).


Which keyword is useful? Var? CS will put it back at compile time, don't worry. And it will do it in more consistent manner than many coders will manually. I have no problems with symbolless maps, and CS will save your ass from being bitten by IE because of stray comma.


I protect my ass with VIM -- any "IDE" likely has an equivalent.

  au BufWrite *.js mark` | silent!%s/,\(\_s*[}\]]\)/\1/g | norm``


> Which keyword is useful? Var?

Yes.

> CS will put it back at compile time, don't worry.

May I ask why you're assuming I'm a moron, exactly?

> And it will do it in more consistent manner than many coders will manually.

I don't know who your coders are, but you should fire them.

> CS will save your ass from being bitten by IE because of stray comma.

Yep, confirmed, and you should probably fire yourself as well.


I came here to post just this. While I like CS, JS isn't this horrid beast people keep making it out to be; often the solutions can be incredibly simple, it's just a matter of thinking about it.


> The JS example is just a very poor example of coding.

That's because it's the output of the coffeescript compiler, it's not "actual" javascript code.


It makes the article very misleading. Yes, CS looks great next to garbage output--but there are about a dozen ways to accomplish these tasks in JS and most are almost as concise as the CS examples.

Honestly, if that type of output is why people think Javascript is "hard" or "convoluted," then maybe CS is right for them. As with any tool, a proper knowledge of its use is required and anyone who would write code like that clearly doesn't really know their tool.


The examples weren't too enlightening.

In the first example I would have written the original handler like this, for example. Written more concisely it doesn't present an immediate need for shortening or clarification by CS:

  document.onkeydown = function(event) {
      var key_to_func = {
	  37: "pan_left", 38: "pan_up",
	  39: "pan_right", 40: "pan_down",
	  189: "zoom_out", 187: "zoom_in"
      };
      for (var i in game_pieces)
	  game_pieces[i][key_to_func[event.keyCode]]();
      return the_screen.refresh(game_pieces);
  };
The class example is better but I haven't found much use for the JavaScript prototypes to build class-like constructs. Instead of using prototyping I generally just create objects and shove stuff into them. YMMV.

Last example is merely a transformation of the first version. The constructs used are quite similar: it would have been nice to see CoffeeScript transform the actual paradigms used in the code. The CoffeeScript version still reads like JavaScript, just with slight syntactic sugar.

Mere syntactic sugar might actually be the core idea of CoffeeScript (instead of landing a somewhat higher-level language on top of JS) in which case the example illustrates nicely why the gains from switching from JS to CS are mostly cosmetic. You can compress your code conceptually much better if you just write JS more functionally, i.e. write a few quick functions to provide map/filter/reduce and some helpers.


I absolutely don't get it. The CoffeeScript code looks way to obscure to me. I don't need more punctuation marks. List comprehensions can just be written in JavaScript very easily. jQuery provides them OOTB. Improved switch statements seems pointless when you can use a dispatch table. I'm all for better OO syntax, but I'd much prefer something slightly more verbose and transparent. This looks like an attempt to put Perl in the browser.


If there's a case for coffeescript over simply biting the bullet and learning to love JavaScript, this blog post doesn't touch it. If anything, well-written JavaScript code would be cleaner than his coffeescript examples (as noted in some commentsl although no doubt his coffeescript could be improved too).


I hate to harp on one minor point that he made, but I strongly disagree with his assertion that you could dump CoffeeScript, stick with the generated JS, and be happy with it. The generated JS he showed in those examples was terrible, and would be a maintenance nightmare.


Anyone planning to make a "CoffeeJava" language?


Had a look at Mirah? http://www.mirah.org/


CoffeeScript is interesting, but to be more so it shouldn't be limited to browser scripting. Until it doesn't become more general purpose i'll just stick to Python or Node.js


CoffeeScript and Node.js get along fantastically. When the "coffee-script" library is loaded, you can even "require('foo.coffee')" directly, making it easy to mix-and-match JavaScript and CoffeeScript files.

Sam Stephenson, the creator of Prototype.js, has been doing all kinds of awesome things with CoffeeScript and Node.js. And there's a chapter of my book on it...


So how do you write a basic web server in CoffeeScript?


The Node.js "Hello World" web server:

    http = require 'http'

    server = http.createServer (req, res) ->
      res.writeHeader 200, 'Content-Type': 'text/plain'
      res.write 'Hello, World!'
      res.end()

    server.listen 3000

    console.log "Server running at http://localhost:3000/"


Well but this isn't written in CoffeeScript. This is what I was saying: CoffeeScript can work alongside Node.js but can't access the filesystem. It might be good in its own right for DOM scripting and such but it doesn't open any more doors than Javascript.


> CoffeeScript can work alongside Node.js but can't access the filesystem.

Um. Node.js gives you access to the filesystem... http://nodejs.org/docs/latest/api/fs.html

And anything you can do in Node, you can do from CoffeeScript code running under Node.


Then what is it written in, if not CoffeeScript. You're somehow very very confused about things :-)


No I think you are confused. That is Node.js syntax. Ok now show me how you open a file on the filesystem in CoffeeScript then.


I'm obligated to respond with the same server in Twisted:

    from twisted.internet import reactor
    from twisted.web import resource, server

    class HelloWorldResource(resource.Resource):
        isLeaf = True
        def render_GET(self, request):
            return "Hello, World!"

    reactor.listenTCP(3000, server.Site(HelloWorldResource()))
    reactor.run()


OT question. How healthy is the Twisted ecosystem these days? Besides it being stable and been around for years, how does the post-divmod horizon looking? Is the whole stack still being maintained?


All of the old Divmod team are still hacking Twisted. It's still maintained, everything gets taken care of, and there's really no doubt about Twisted's future.


Quite a lot of examples of CoffeeScript with Node.js here: https://gist.github.com/978411


It's not limited to browser scripting. You can use it with node: https://github.com/sstephenson/node-coffee-project


You can use it anywhere you use JavaScript, including Node.


And how will Node interpret it?


So well, then you write something in CoffeeScript, "compile" it to Javascript and run it in Node.js? Ugly dev cycle, maybe worth hooking to Eclipse.


I fail to understand how this gets to be on the first page of HN, sided by links to the usual great stuff. My opinion worths for what it worth, but I would say this is a rather bad article and show some poor programming examples.

As many pointed out (five proper versions of the first snippet so far), the examples are not really valid.

I do not necessarily agree with the trend of criticizing javascript of being a language that needs to be fixed. Quite frankly, it is a proper language. Coffescript introduces some practical sugar, class construct being IMHO the best plus as javascript class definitions are rather verbose. CS object notation is another neat advantage. Many other things on coffeescript appear to be concepts that for some reason are fashionable these days such as the arrow or the conditions after the statement.

I write javascript everyday and I absolutely don't feel like there are anoyances which need to be taken care of urgently. It's a language as enjoyable as most. Cofffescript does look cool and I do plan to use, at least for fun if not for any other reason. But saying that is the missing piece, or that is the long expected replacement for javascript, sounds like fanboyism.


> I fail to understand how this gets to be on the first page of HN

Same reason every shitty go article out there reaches HN's front page (or reached until pretty recently), same reason /r/prog's frontpage is cyclically full of a single technology or language: it's what most denizens of the place currently have a hard-on for.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: