By example: Continuation-passing style in JavaScript
matt.might.net
matt.might.net
ECMA doesn't require efficient tail recursion. Caveat programmer.
You can work around it with a trampoline (http://en.wikipedia.org/wiki/Tail_call#Through_trampolining), which is what I did with cfa-js. (http://dl.dropbox.com/u/6600185/cfa/cfademo.html)
If you're going to do real CPS programming in JavaScript, you'll want to use a trampoline or a timer mechanism to reset the stack.
But, in most cases, you do a little computation and then block waiting for an event.
When the continuation gets invoked by the event-handling loop, the stack is reset.
You're not compiling to C here: "the stack" may be a chain of heap-allocated activation records that are still live as far as GC is concerned. What you're describing might work but you'd want to test it to be sure.
EDIT: On reflection I agree that the setTimeout() approach should free the stack, however it's implemented.
You can always use setTimeout to call your tail, which has the neat effect that your CPS "threads" become cooperatively multitasked and can progress alongside each other. Just replace "foo()" tail calls with "setTimeout(foo, 0)".
And the downside that you're nuking your stack every time, making debugging quite a bit harder.
This type of thing has a few nice properties -- you can easily register more than one callback, as well as using them as a pipeline (kind-of like monads in haskell), which are awkward to implement with callbacks.
As an aside, in python you can trick generators into turning synchronous-looking code into asynchronous, which can actually make things look a lot neater if done properly -- i'll provide code if anyone's interested.
Example: http://dieselweb.org/lib/ or https://github.com/saucelabs/monocle
Although Diesel2 is much more interesting, with greenlets support. No more messing about with `yield`, since the blocking functions themselves force a context switch. https://github.com/jamwt/diesel
I wish I'd found diesel before twisted; it seems a lot more elegant if you don't need any of the built-in protocols. The twisted model is good, there's just too much of it.
function callcc (f,cc) {
f(function(x,k) { cc(x) },cc);
}That's the implementation of call/cc.
For how to use call/cc, I recommend my other article on continuations:
http://matt.might.net/articles/programming-with-continuation...
If you posted a simple example of JavaScript written in continuation-passing style and using this call/cc implementation probably everything would be clear.
http://blogs.msdn.com/b/ericlippert/archive/2010/10/21/conti...
They're more from a language design perspective rather than the more practical article here.
Continuations become a highly constrained kind of callback in continuation-passing style.
Callbacks by themselves don't make for a nice intermediate format for compilation, or a way to model exceptions or distributed computation.
They only gain these abilities when constrained as continuations in CPS.