There's currently an open pull request for Iced to be merged with CoffeeScript, and the merge will happen if the fork manages to:
* Provide a seamless veneer over sync patterns in async code: defer in loops, with try/catch, within the middle of an expression...
* Handle both synchronous and parallel style "maps"...
* Prove to be more pleasant / powerful to use than callbacks and promises, in practice.
* And then the hardest bit -- do it all without breaking CoffeeScript's golden rule: it has to compile into straightforward JavaScript -- i.e., the callbacks you would have written in the first place.
It's a tall order, but if Max manages to pull all that off, it'll be pretty great.
streamline.js already fits the bill, including your golden rule: the core idea was precisely to generate the callbacks that the developer would write otherwise.
Today it works with both CoffeeScript and Javascript, but in a decoupled way: the streamline transformation is applied to the JS generated by the CS compiler. It could easily be repackaged as a CS language extension.
It has been around for more than a year and the CS+streamline combination is powering at least one live site: http://www.thethingdom.com/
Bruno
That being said, right up front he explains the two reasons why this is a departure from CoffeeScript’s core philosophy: `await` and `defer` are changes in the semantics of JavaScript, and not surprisingly, code written in IcedCoffeeScript no longer compiles into more-or-less recognizable JavaScript.
Modulo syntax and sugar, CoffeeScript is JavaScript with some minor changes to the AST (such as soaking up nils with the Elvis operator). IcedCoffeeScript is another language that evolved from JavaScript and transpiles to JavaScript.
Before adding such features willy-nilly to CoffeeScript, folks need to be comfortable walking away from a core value. I’m not saying that’s a bad thing, but it’s a problem related to the Innovator’s Dilemma: To embrace this new value, CoffeeScript will have to alienate some portion of its user base that value compiled JavaScript that is easily recognizable to anyone who has read the CoffeeScript source.
But one objection we will both encounter repeatedly is that if a language supports feature X, and you work on a team/inherit code/use libraries, there will always be people who don’t want to use feature X but wind up dealing with it any ways because someone else used it.
So the naysayers will claim that if it’s added to the core language, it’s only a matter of time before they wind up dealing with it whether they like it or not. Given the way that most Ruby users have to live with monkey-patching in Ruby whether they do it themselves or not, I fully expect that if await and defer are added to core CS, there will be libraries or CommonJS modules that use it and presto, you may find yourself looking at some of its output in the debugger one day.
I’d rather use the feature myself and benefit from it, but I can accept what the luddites are saying even if I don’t end up coming to the same conclusion about whether the feature should be added or withheld :-)
UPDATE: I recall being told that tail-call optimization should not be added to certain languages because it encourages programmers to write recursive functions that are “hard to read.” You might argue that if you never write recursive functions, why should you care? But people do care.
Confused? Here are the iterative, recursive, and itero-recursive ways of writing the factorial function:
function fact_r(N) {
return N <= 0 ? 1 : N * fact_r(N - 1);
}
function fact_i(N) {
var acc = 1, i;
for (i = 1; i <= N; i += 1) {
acc *= i;
}
return acc;
}
function fact_ir(N, acc) {
acc = acc || 1; // default value of acc
return N <= 0 ? acc : fact_ir(N - 1, N * acc);
}
So the accumulator variable becomes an argument variable which may have to be separately initialized. The key feature which makes something optimized for "tail calls" is that when f(something) wants to recurse, it returns f(something else). In other words, certain conditional logics can happen, but don't leave an operation like the "n " in "n recurse(n - 1)" on the stack or else you can't get rid of the function call. It's not even a special optimization -- basically you can move one line in the compiler a couple steps ahead of where you might have automatically written it, and it suddenly treats iteration and tail-recursion the same.So that's the difference. If you can understand fact_ir, which does the iterative idea in a recursive syntax, then you can understand tail recursion.
So this is a more fundamental fork than first appears... And it explains why it includes debugging information (loading up the code examples reveals artifacts like lineno: 6 in the output JavaScript).
I recall a debate about debug info, turning on the values you note. Can't find it right now. (though, apparently extra-linguistic Source Maps are being introduced into browsers http://news.ycombinator.com/item?id=2862629)
Personally, I think "clean JavaScript" is the right adoption strategy, for now, in a JavaScript-dominated world. When CoffeeScript has native browser support, that value can be dropped (it might not happen: MS supports VB, Google supports Dart... even if Mozilla supports CS, the others mightn't).
Finally, I don't think the resulting JavaScript is that hard to understand - you can easily see the correspondences with the source. However, it's the thin-edge of the wedge. What changes will be added next?
As someone else said, if you avoid await/defer entirely, it seems to back off to exactly what Coffeescript does, so there is a nice progression path.