10

Continuations are extremely useful in functional programming languages (e.g. the Cont monad in Haskell) because they allow a simple and regular notation for imperative-style code. They're also useful in some older imperative languages because they can be used to implement missing language features (e.g. exceptions, coroutines, green threads). But for a modern object-oriented language with support built in for these features, what arguments would there be for also adding support for first-class continuations (whether the more modern delimited style reset and shift or scheme-like call-with-current-continuation)?

Are there arguments against adding support other than performance and complexity of implementation?

15

Let's let Eric Lippert answer this one:

The obvious question at this point is: if CPS is so awesome then why don’t we use it all the time? Why have most professional developers never heard of it, or, those who have, think of it as something only those crazy Scheme programmers do?

First of all, it is simply hard for most people who are used to thinking about subroutines, loops, try-catch-finally and so on to reason about delegates being used for control flow in this way. I am reviewing my notes on CPS from CS442 right now and I see that in 1995 I wrote down a profQUOTE: “With continuations you have to sort of stand on your head and pull yourself inside out”. Professor Duggan (*) was absolutely correct in saying that. Recall from a couple days ago that our tiny little example of CPS transformation of the C# expression M(B()?C():D()) involved four lambdas. Not everyone is good at reading code that uses higher-order functions. It imposes a large cognitive burden.

Moreover: one of the nice things about having specific control flow statements baked in to a language is that they let your code clearly express the meaning of the control flow while hiding the mechanisms – the call stacks and return addresses and exception handler lists and protected regions and so on. Continuations make the mechanisms of control flow explicit in the structure of the code. All that emphasis on mechanism can overwhelm the meaning of the code.

In the next article, he explains how asynchrony and continuations are exactly equivalent to one another, and goes over a demonstration of taking a simple, (but blocking,) synchronous network operation and rewriting it in asyncronous style, making sure to cover all the hidden gotchas that have to be covered in order to get it right. It turns into a massive mess of code. His summary at the end:

Holy goodness, what a godawful mess we’ve made. We've expanded two lines of perfectly clear code into two dozen lines of the most godawful spaghetti you've ever seen. And of course it still doesn’t even compile because the labels aren’t in scope and we have a definite assignment error. We;d still need to further rewrite the code to fix those problems.

Remember what I was saying about the pros and cons of CPS?

  • PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
  • CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
  • CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
  • CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else is – check.

This is not some intellectual exercise. Real people end up writing code morally equivalent to the above all the time when they deal with asynchrony. And, ironically, even as processors have gotten faster and cheaper, we spend more and more of our time waiting for stuff that isn’t processor-bound. In many programs, much of the time spent is with the processor pegged to zero waiting for network packets to make the trip from England to Japan and back again, or for disks to spin, or whatever.

And I haven't even talked about what happens if you want to compose asynchronous operations further. Suppose you want to make ArchiveDocuments an asynchronous operation that takes a delegate. Now all the code that calls it has to be written in CPS as well. The taint just spreads.

Getting asynchronous logic right is important, it’s only going to be more important in the future, and the tools we have given you make you “stand on your head and turn yourself inside out” as Professor Duggan wisely said.

Continuation passing style is powerful, yes, but there's got to be a better way of making good use of that power than the code above.

Both articles, and the following series on a new C# language feature that moves all this mess into the compiler and lets you write your code as normal control flow with a special keyword to mark certain parts as asynchronous, are well worth reading even if you're not a C# developer. I'm not, but it was still quite the enlightening experience when I ran across it for the first time.

  • 4
    It's worth noting that if your language supports syntax extensions like macros, then continuations become far more useful because you can hide the mechanisms for implementing various kinds of interesting control flow inside the syntax extensions. Which is basically how "await" works anyway, it's just defined in the language since C# doesn't provide the tools to define those sorts of syntax extensions yourself. – Jack Apr 11 '15 at 3:11
  • Good article, although I'll note that CPS and first-class continuations aren't the same thing exactly (CPS is what you do in a language with no support for continuations when you find you do need them). It sounds like C# is going down exactly the same route I'm thinking about (although I can't decide if I want to automate tyre transform to CPS or implement full-blown stack-copying delimited continuations). – Jules Apr 11 '15 at 18:12
  • @Jack - that's exactly what I'm considering right now: the language I'm designing has a macro facility that could support the CPS transform automatically. But full native continuations might give better performance... the question is, how often would they be used in a performance-critical situation? – Jules Apr 11 '15 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.