[ Long for a post, short for an essay. Sorry, the language critiques pages on minsky.med are down -- it's disk is hosed - and I have some notes from when I originally researches this question while doing the ceval.c hacking referred to, but I don't have time to dig them out at the moment, so I can't fill in all of the references as I would like to. ] "The continuation for some computation is whatever comes after it, expressed as a function of the result for that compututation." R.D.Tennet, Principles of Programming Languages, 1981 More intuitively, you can think of a continuation as the abstraction of a "place" in a program -- not just a label or address, but more like a debugger breakpoint -- there is a machine state that goes along with that location which captures the state of the computation. Along with expressions, definitions, commands, state, etc. its one of the abstractions that all programming languages are built upon. I state this up-front because, like subroutines or identifiers, there is the abstract idea and then how their implementation in various languages. A language that doesn't provide explicit continuations still has continuations ( just as a program written in a functional language that doesn't provide explicit commands to handle state, still HAS a state. ) and a language that provides something called continuations may provide something that isn't quite what that definition at the top would lead you to expect. ( The difference is how global is the state that is saved. Except for complete transaction rollback, you don't usually WANT to consider the entire global effect of the computation. ) Sequencer are the things that control program flow. The most basic sequencer is often represented as ";" in books on programming language semantics. It means, basically: do the next thing. You can think of it as the null sequencer. JUMP or GOTO is the sequencer that changes the program location without saving any continuation. CALL saves the current continuation somewhere ( typically, on a stack ), and jumps to a new location, also possibly creating a new temporary local state or context for execution to begin. RETURN jumps back to the saved continuation, without saveing the current continuation. RESUME and SUSPEND are the pair that implements what I believe Knuth calls semi-couroutines ( I can't find the reference at the moment, but I have some notes around somewhere. ) RESUME is really the same as CALL ( except... ) SUSPEND is like return except that it saves the current continuation somewhere. Where that somewhere is and how it's expressed varies in different program languages, but it has to be somehow returned or linked to the target or label or the RESUME command. That target expression is really a variable that jumps to the previously saved continuation. [ This is what Icon calls co-expressions or generators. ] CALL/RETURN & RESUME/SUSPEND are both asymetrical -- the callee always eventually returns to the caller site, and two different types of syntax elements are usually required to distinguish the pair of sequencers. Full coroutines are completely symetrical: save the current continuation state and jump to a new or restored context. If you add a couroutine to manage a list of ready-to-run continuations, then you have cooperative multitasking. If you add an way to externally trigger the saving of a continuation and the suspension of a thread without requiring it to explicitly suspend execution, then you have preemptive multitasking. If you have a way to explicitly handle continuations in a programming language, then you can build coroutines, threads, objects, generators, and all of the various flavors of exception handlers. Very powerful. Just like goto's. And just as with goto's, it may be much less confusing to avoid "raw" continuations and use threads, coroutines, objects, and exception handlers. Part of the very idea of structured programming, is after all, to restrict your choices. Just as trying to structure words into the sonnet form, or writing to a particular musical form helps with the flow of creativity onto the empty page, the doctrine of structured programming says that 80% of the things you could possibly ever want to do can easily fit into a handful of control structures ( and those that don't fit easily can be splooged without too much violence ). Add exception handling and threads and your probably at 95%. [ I wanted to get back to the 'goto' thread somehow! BTW: Knuth wrote the definitive reply to D--- describing why and when goto's are necessary, however, when you add 'break', exception handling, and some sort of finalization ( unwind-protect in Lisp or Python's try/finally ), I believe you cover 99 % of the cases. Also: someone ( in that, or some other past thread ) mentioned "the mythical 'COME FROM' statement". INTERCAL actually does have a 'COME FROM'. INTERCAL's implementation of COME-FROM, like everything else in INTERCAL, is a joke, of course. But when I first heard of the feature, I thought it was serious and I tried to think of a reasonable implementation. What I came up with was to require matching GOTO's and COME-FROM's as a way to impose some discipline on "spagetti code". INTERCAL's version is better. If you're intent on writing incomprehensible code, what is better than a feature that allows the flow of control to suddenly drop through a trapdoor and magically appear pages later when the magical incantation is used. Must have been written by the same guy that wrote 'Adventure' ! ] Anyway, saving all of these continuation states to go back to later imply something more than a single stack - either heap allocated frames of multiple stacks. The Python virtual machine happens to have heap allocated frames, which are almost sufficient to implement continuations. I hacked up an experimental version of ceval.c, and had to add only one field to frameobject, and a couple of lines of code to implement 'suspend' and to make sure that all of the fields were properly updated before suspend. Frameobject's are regular python objects ( although they aren't usually visible at the Python level except in exception stack traces. ) so they are reference counted and aren't disposed of while there is a dangling reference to the suspended continuation. This did manage to implement a form of generator/semi-coroutine, however, the problem with implementing threads or coroutines in that Python and C can and do call each other, and the recursive implementation of ceval.c made stack cleanup impossible if you didn't always eventually return to the calling site. [ This is when the idea of a 'flatter' implementation of the virtual machine, due to be in Python 2.0, was discussed -- although Guido had already been considering it for effeciency reasons. ] As to the question about what can you do with coroutines that you can't do with objects ? Not much, really. But that's not the right question. ( I can't disprove the assertion that anything worth saying can be stated in the form of a limerick, either. ) You only NEED a single type of loop mechanism, but we actually use several. Objects in Simula were actually implemented as coroutines. One of the history of Lisp papers makes it clear that the Scheme group and Alan Kay's Smalltalk group were in communication and agreement about the complementarity of Smalltalk objects and Scheme continuations. They had discussions about how they were both different ways of looking at the same thing. Sometimes one is a more natural notation that the other. Icon's coexpressions for example, do in a single line what takes a dozen or more lines of Python code. I don't, however, think that conciseness is the main virtue, so much as that it's often a convenient and simple way to think of a problem. The typical unix command line pipeline is just a series of a generator, a buch or filters and a sink for the final data, all linked together as coroutines. ( Well, really processes, but processes, threads and coroutines are all variations on the same thing. )