Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Await and Defer in Coffeescript core (github.com/jashkenas)
75 points by DanielRibeiro on Dec 18, 2011 | hide | past | favorite | 25 comments



Thanks for the last link[1]. It is pretty amazing. I've studied some Scheme and call/cc on college (and played around with it on Ruby), but this post made me realize how much more powerful can continuations be (besides doing basic control flow/exception stuff).

A post that stands on its own, whether you like/care about Coffeescript/Javascript or not.

[1] http://gfxmonk.net/2010/07/04/defer-taming-asynchronous-java...


Dude you are awesome. I used to think splats and destructuring were what made CoffeeScript more than just pretty Javascript. But this new stuff is doing even more substantive work on our behalves. Thank you.


Thank @maxtaco -- TameJS is his baby.


The defer syntax feels wrong to me, I think because it looks like a function call but doesn't behave like one (because it assigns to its arguments). If it behaves different then it should look different.

Since it creates a continuation, perhaps it should resemble the syntax for creating a function, like this:

    await resolve "host.com", (ip, err) ~>
    console.log "#{ip} #{err}"
or this:

    await resolve "host.com", (ip, err) <-
    console.log "#{ip} #{err}"
    
Introducing an operator like this would also avoid some name collisions.

And does the callcc really have to be able to assign to things? Make it work just like a formal parameter list, and then you can actually compile it into one, avoiding all that pass-by-reference hoopla.

The autocb thing is even weirder. Magic variable names? I've never heard of a language that resorted to this. Why not just some new syntax to mark a parameter as the callback? Perhaps some postfix modifier, to match the splat syntax?

    resolve = (host, callback <-) ->
      ...
      return ip, err
or overload a keyword?

    resolve = (host, return callback) ->
      ...
      return ip, err


If I understood correctly, they would become keywords, like 'return', 'true', 'false' are in javascript, and 'class' is in coffeescript. The problem with it is that any functions called defer would have to be renamed in order to work on this version of Coffeescript, possibly breaking some existing code.


This is really pedantic, but I also think it's important: class is a keyword (well, really just a reserved word) in JavaScript as well. The reason it's important is that some engines will actually have issues if you use syntax like this[1]:

    var obj = {class : "blarg"};
[1]: https://developer.mozilla.org/en/JavaScript/Reference/Reserv...


Yeah, we are kinda splitting hairs here, but strictly speaking, 'class' is reserved word in JS. It will become a keywords when they start using it. The link you provided does make this distinction: reserved as future keywords.


This is a game changer, in my opinion.


Positive or negative?


Positive.


For those who didn't understand the new constructs, the following excerpt from TameJS[1] website helps:

> `await` marks a section of code that depends on externals events, like network or disk activity, or a timer. An `await` block contains one or more calls to `defer`. Calling `defer` constructs a new deferral. Only once all deferrals inside an await block are fulfilled does control continue past it. `defer()` behaves much like a normal function; it returns an anonymous function that you give to your async functions. These functions "fulfill" their deferrals by calling the callbacks passed to them.

[1]: http://tamejs.org/


I love CoffeeScript, and I would hate to see this become part of the language specification.

Just a few things off the top of my head:

- Why does defer() look like any other function.

- Completely changing the return keyword's meaning if "autocb" is a function argument is just silly.

- The generated JavaScript is utterly unreadable and unquestionably comes with a performance trade-off.


Is this just saving some tabs, or is it cooler than that? Will a return return to the top function? Will exceptions get caught across callback boundaries?


Please, no. I'll stop using coffeescript the day the compiled JS becomes unreadable at first look. A bundled flow-control library with special syntax privileges would be more feasible, and prevent mangling the output.


Don't worry -- this is currently just a pull request and a fork that you can try out:

http://search.npmjs.org/#/tamed-coffee-script

The real challenge here is to see if this branch can be made to generate the asynchronous code you would have written in the first place ... or how close we can come to approaching it.


I think it is a cool thing to be considered.

Mostly because you can already do it with runtime AST metaprogramming (which is very doable in JS, due to Function.toString, even though is not as easy to do as Lisp/Clojure's macros), and have it as lib.

What the language support adds is simply the convenience of not passing a function with the block of code that will be instrumented. In fact, with language support, you can even debug it.

And the best part about this change: if you don't use it, it will not affect you at all. It is opt-in by default.

Not saying we should take this change lightly, but I believe it does have many merits, and we should not be too quick to dismiss it.


I’m a huge fan of stuff like this, however it’s not strictly true that features like this are “opt in.” If you write your code alone and in a vacuum, it’s opt-in. However, if you write your code on a team, if you use NPM packages that were compiled from this code, if you use backbone extensions compiled from this code, you’re opted in “by proxy” to the code it produces whenever you find yourself stepping through the code in a debugger or reading the compiled code in a browser environment.

IMO, it could be a win by virtue of producing consistent code for the most common cases. It may seem unfamiliar at first compared to the code you would personally write, but if it becomes a standard and everybody’s code ends up looking much the same, that’s a win overall.

I think this is already true for binding functions to the current ‘this’ and for comprehensions, and we could use a standard ‘pattern’ for the resulting asynchronous code for most common cases.

But I grant that it isn’t purely optional in a world where very few people write their own stack in its entirety.


Ever since I wrote this I realized that this feature, as outlined here, is not opt-in, even in a vacuum, due to the fact that will break all variables named 'await' and 'defer'.

About using others' code: compile time code generation is always possible, and you can't prevent the libraries you are using from using it. It can even be worse: they can runtime code generation, or a downright obfuscator.

Since more and more languages are being compiled to JS (Clojurescript being one formidable recent addition), trying to stay "pure" with respect to the libraries you use is getting harder and harder.

My biggest worry is that Coffeescript starts stagnating from lack of innovation (which seems to affect most stable languages).

One way Scala managed to experiment with features while still trying to be stable, was through Scala Compiler Plugins[1].

This we could be having a less theoretical conversation, by throwing it in the wild, and learning from the experiments people make with it.

[1] http://www.scala-lang.org/node/140


Isn't that achievable by using a symbol that just causes the next adjoined statements to be considered an indent block? Like the inverted or tilde arrow often brought up:

    async.knead flour, water, (err, dough) ~>
    return log err if err?
    async.bake dough, (bread) ~>
    eat bread
BTW, was looking back at the original announcement here at HN, and found this:

    CoffeeScript + node.js + V8 could produce one amazing
    language and implementation.
Little did he know... http://news.ycombinator.com/item?id=1014080


Would your opinion change if you could debug CS natively?


Probably not. I still think in javascript and rely on the assumption that I can peek at the compiled output anytime. Looking forward for source-mapping though :)


How would this even work? I'm curious.


The same way debugging between machine code and source code works when all you really have from the processor is an instruction pointer. :D

You maintain a two-way mapping between file names/line numbers in the CS source and file names/line numbers in the JS source. When the debugger throws an exception with a line number, just translate it before presenting it to the user. For break points and stepping, go in the opposite direction.


Does this work with the Promise API without defer?




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

Search: