Continuations are the new threads

I’ve always thought that continuations are the right answer to many if no all the network-programming problems.

What are continuations?

A continuation represent the state of execution of a function: all the local variables and the instruction pointer (the last line executed).
(Update: At least: there can be many kind of continuations. Other than the local variables, it’s possible to store the state of the entire thread/process/stack)

What are them used for?

You can interrupt the execution of a function and resume it later.
This can be useful to create “generators“: iterators made simple.

A generator looks like this:

function next() {
  for(x=0; x < arr.lenght; x++) {
     yield arr&#91;x&#93;;
  }
}
&#91;/js&#93;

The function <code>next</code> return the next element in the <code>arr</code> array every time is called. 

When you call the function you resume the continuation, so the execution will resume from the <code>yield</code>.
If you want to implement an iterator like that without generators you'll have to explicitly unroll the <code>for</code> loop.

The resulting code will be surely less readabe.
Like:
[js]
function next() {
  if(this.current == null) this.current = 0;
  this.current++;
  if(this.current > arr.lenght) {
     this.current = 0;
     return null;
  }
  retrun arr[x];

This can get really worse for every non-trivial iterator (eg: tree traversal).

Another nice use of continuations is to generate cooperative threads.
They are different from standard threads because they can’t be stopped, they should cooperatively stop.

For example you can do:

function process() {
  sendConnectRequest();
  yield;
  if(!this.connected) return false;
  sendLoginRequest();
  yield;
  sendMessage();
  yield;
  trace("msg received: " + this.response);
}

The process function return the control to the calling function every time it has to do an asynchronous operation.
Without continuations you’ll have to do:

function process(onComplete) {
  connect(function (connected) { 
      sendLoginRequest( function(logged) {
          sendMessage( function (response) {
              trace("msg received: " + response);
              onComplete();
          }
      }
  }
}

As you can see we have used closures in a Continuation Passing Style:
Every time you have to do an asynchronous operation, you’ll have to pass a function (a callback) that will be called when the operation is done.

Without closures the code will look even worst, this is why recently there is a great buzz about closures in the java world.

But, what have in common continuations and threads?

A continuations-like style of programming (but without continuations) is possible only using threads.
However, threads are the heavier and most problematic way to handle the I/O waitings. (see: 10Kproblem)
You have not only to deal with concurrency problems with read/write variables, but you have to block an entire thread for every I/O operation.
A better way is to use asynchronous events, like in the last two example. But of the two, which one do you prefer?

If your answer is like mine, take a look to:

  • Jetty servlet engine, it support a limited form of one shot continuations: the function is re-executed from the beginning, what is restored is only the HTTP connection state.
  • RIFE continuations, it’s a bytecode-level implementation in Java, these are real continuations
  • NarrativeJS, a precompiler that create a simple form of continuations in Javascript: using the -> operator the execution can be resumed from that point.
  • Update: Torsten Curdt suggest Javaflow, a continuation implementation that seems really interesting, he offers some tutorials on his blog too

Jetty scratched only the surface of what is possible with continuations, while with RIFE it’s possible to develop highly concurrent servers using cooperative sessions instead of threads (some >10000 connections instead of 1000). See: Mina Java-NIO library, Perl POE, Mina-Mule.

A server that uses continuations, will allow the simple and linear programming style of blocking-synchronous operations while using non-blocking ones. With a huge increment in performance.

In the client side, the most interesting is NarrativeJs, that use an approach that can be adapted to every language:
it unroll loops and transform all local variables in object variables to generate his quasi-continuations.

function boo(){    
  alert("start");
  sleep->(30);
  alert("end");
}

became:

function boo(){var njf1=njen(this,arguments);nj:while(1){switch(njf1.cp){case 0:
alert("start");njf1.pc(1,null,
sleep,[30]);case 1:with(njf1)if((rv1=f.apply(c,a))==NJSUS){return fh;}
alert("end");break nj;}}}

The code looks ugly but it is so to preserve line numbers.
With narrativeJs you can do complex animations in a simple, linear way:

waitForClick->(theButton);
animate->(theButton, "left", 200, 1000, 20);
            
theButton.innerHTML = "go left";
    
// move the button to the left (again note the blocking operations)
waitForClick->(theButton);
animate->(theButton, "left", 0, 1000, 20);

or:

document.getElementById("myElem").innerHTML = fetch->("http://www.url.com/");

Really interesting now that Flash and Ajax are pushing asynchronous operations to the masses.
Will we say goodbye to events, and maybe to threads as we know now?

13 thoughts on “Continuations are the new threads

  1. Madarco says:

    Thanks, you are right,
    that was a simple example, maybe I should have used the term “escape continuation”, but I’m not sure on the terminology.
    What I was talking about in this article is the usefulness of some kind of continuations, even when the state preserved is only the one of the function or the HTTP connection and not of the entire thread.

  2. Fabien says:

    What you describe are coroutines, a.k.a. *one shot* continuations.

    Full continuations can not only be paused and resumed, they can also be duplicated and reset/relaunched. Arguably, that’s often more rope than required to hang oneself. Their best know “reasonable” usage is in web-based applications: by using “open in new window” and “back”, the user can go back in time and/or duplicate a session while it’s running; this is mostly what full continuations allow to do seamlessly.

  3. Pesho says:

    Amigo, you are most deeply misguided. Your post gets continuations mostly wrong, and what little it got right has been smeared beyond recognition through sloppy “examples”. Read a book. Write a program. Thoroughly understand what you did. Only then go preach to others.

  4. John Nowak says:

    Not to offend Madarco, but why not write on things you know? This post is so error ridden as to be actively harmful.

  5. Pesho says:

    @John: My thought exactly, offense or not.

    @Madarco: I believe I posted a very good suggestion to you. Hate to tell you twice, but you haven’t the slightest idea of what you’re talking about. There may still be hope for you though — just go and sort this thing out in your head first.

  6. Madarco says:

    Thanks for you interest, but what’s the utility to state that I’m wrong without at least explain what exactly?

    Aren’t generators a kind of continuation?
    Aren’t continuations an elegant way to do iterators?
    Doesn’t Jetty “continuations” allows to resume only the HTTP connection?
    And doesn’t narrativeJS allows to do simple “continuations” without language support?
    Doesn’t those “simple continuations” allows to do lightweight cooperative threading?
    Aren’t those “simple continuations” (one-shot & escape continuation, call as you like) an easy way to simplify the life with events?

  7. Pesho says:

    Sigh…

    I played around with the thought of answering your questions, but gave it up almost immediately. Those aren’t the right questions. Heck, those questions aren’t even WRONG :)))

    The main point of continuations is that they replace the usual call-return model in which we describe program structure. Let’s now revisit some of your questions:

    – Aren’t generators a kind of continuation?
    – Aren’t continuations an elegant way to do iterators?

    Maybe, but who cares? Everything that has to do with execution control can be seen as a kind of continuation, but that would be arbitrarily far-fetched, depending on the case.

    The example of *iterator* you gave (not a generator) is just as easily understood in terms of call-return as it is in terms of continuations. Note that, in your discussion, you’re reasoning in terms of call-return (functions, calls, etc.) — where exactly do continuations come in?

    Furthermore, the explanation there is just plain wrong, with a lot of uncertainty about who calls what and when, and I’m not going to talk about the loop unrolling reference that you must have thrown in for good measure — it’s so out of place, with simply nothing to do with the “case”.

    See, I would gladly send you “corrections”, if only I could reasonably start somewhere. However, it would be much, much more trouble than explaining continuations to someone who has never heard of them — that’s because, pretty much along the lines of what John said above, your post is so wrong it actually makes people dumber.

    It is not just the problem of missing the target, because that would imply having aimed at the target in the first place — rather, it is the fundamental problem of preaching what you don’t understand. You’re not in the need of being corrected, you obviously need a radically different approach to knowledge and understanding.

  8. Madarco says:

    You continue to miss the point of this post.
    You should have at least checked the links, to understand the “loop unrolling” thingy or where the examples came from.

    Sadly, since you didn’t even mention “cooperative threading” (hint: THIS is the point),
    continue to discuss only the minor aspect of the formal definition of “Continuations” (which, as you said, is maybe inexact, but not wrong)
    and use much more words to state that I’m wrong than to explain why,
    make me fear that you are more interested in a monologue on you formal knowledge of continuations that on an argumentation on the future implications of some form of continuations in asynchronous programming.

  9. Pesho says:

    “You continue to miss the point of this post.”

    That’s because you have concealed it so magically, I guess.

    “You should have at least checked the links, to understand the “loop unrolling” thingy or where the examples came from.”

    By all likelihood, I’ve known about loop unrolling since when you could still walk upright under the kitchen table, kemo sabe, and I know it has nothing to do with continuations, perhaps except for in Stephen King stories.

    “Sadly, since you didn’t even mention “cooperative threading” (hint: THIS is the point),
    continue to discuss only the minor aspect of the formal definition of “Continuations” (which, as you said, is maybe inexact, but not wrong)”

    I didn’t, because I gather you know about cooperative threading about as much as you know about continuations — and talking about “the minor aspect of the formal definition of “Continuations”” is like talking about the minor aspect of the formal definition of addition 🙂 You can’t talk about something if you have no idea what it is.

    “and use much more words to state that I’m wrong than to explain why,”

    Granted, I haven’t been any more successful in explaining to you why you’re wrong than I would be explaining to a blind man the concept of color. Don’t blame it on me though…

    “make me fear that you are more interested in a monologue on you formal knowledge of continuations that on an argumentation on the future implications of some form of continuations in asynchronous programming.”

    I can see you’re still missing the point, and looks like you wouldn’t see it if it hit you on the nose. Not that I’ve offered tons of “formal knowledge” here, but in case you’re deluded about that too — formal knowledge isn’t a dirty phrase. Your ravings about future-implications-blah-blah are of little value if you can’t get simple things straight in the first place.

    BTW, my advice still holds — more reading and listening, less talking and preaching.

  10. Madarco says:

    Your comments are totally unrelated from the argument of this post.

    My advice still holds: this post is about the future implications of some forms of continuations in asynchronous programming and not your intimate thoughts on me.

    My excuses to my readers for being part of this flamewar.

Comments are closed.