6 ms·
Why I'm (Finally) Switching to CoffeeScript
- TrevorBurnham 15y agoSome 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.
- chime 15y agoThe 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
- BMorearty 15y agoTo be fair, lack of optimization by a compiler is not a bug--unless it claims to do that kind of optimization.
- TrevorBurnham 15y agoWell, there was a related inconsistency which was certainly a bug. And it's now fixed: https://github.com/jashkenas/coffee-script/issues/1669 https://github.com/jashkenas/coffee-script/issues/1669
- gmac 15y agoI 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).
- masklinn 15y ago> 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)
- M1573RMU74710N 15y ago> 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.
- eekfuh 15y ago> 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.
- reaktivo 15y agovar 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); });
- Klonoar 15y agoI 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.
- rimantas 15y agoThis 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.
- masklinn 15y agoWhere "more terse" means "removing a useful keyword and replacing parens by spaces". Not exactly impressive (and coffeescript's symbolless maps read horribly badly, imo).
- deleo 15y agoCoffeeScript 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
- phren0logy 15y agoYou can use it anywhere you use JavaScript, including Node.
- simpsond 15y agoIt's not limited to browser scripting. You can use it with node: https://github.com/sstephenson/node-coffee-project https://github.com/sstephenson/node-coffee-project
- TrevorBurnham 15y agoCoffeeScript 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...
- deleo 15y agoSo how do you write a basic web server in CoffeeScript?
- pilif 15y agoWhen 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.
- darkxanthos 15y agoI 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.
- nextparadigms 15y agoAnyone planning to make a "CoffeeJava" language?
- groovy2shoes 15y agoHad a look at Mirah? http://www.mirah.org/ http://www.mirah.org/
- yason 15y agoThe 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.
- tootie 15y agoI 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.
- dancavallaro11 15y agoI 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.
- Stratoscope 15y agoAt 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.
- skeptical 15y agoI 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.
- masklinn 15y ago> 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.
- podperson 15y agoIf 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).