A Taste of 2.8: Continuations

Scala 2.8 will support continuations, a powerful control flow abstraction. Among other things, continuations allow using callback oriented, event driven APIs in a direct, statement-by-statement style.

This will potentially simplify a number of programming tasks:

doing asynchronous I/O using Java NIO

using executors and thread pools

handling cross-request control flow in web applications

There won't be any new API's for these use cases in Scala 2.8, but we expect them to emerge once the underlying continuation machinery is in place.

A Glimpse of Shift and Reset

A continuation, "captured" at some point in a program, embodies "the rest" of the program at that certain point. The interesting part is that continuations can be treated as regular function values, e.g. saved in a data structure and invoked only later, or not at all, or multiple times. Continuations in Scala differ from those available in languages like Scheme or ML, in that Scala's continuations are composable (or "delimited"). This means that they embody not the whole rest of the program, but just a partial rest, up to a programmer-defined outer boundary.

Continuations are manipulated by means of two primitives, shift and reset. Calling shift captures the current continuation, and reset defines the boundary up to which the continuation reaches. Here is an example:

Of course, shift and reset are library functions, not new keywords. They also need not appear as close together syntactically. In fact, corresponding uses of shift and reset may occur in completely unrelated methods. The type system ensures that nothing goes wrong and requires every method that contains a free shift to be marked with a type annotation @cps on its return type. For many cases though, this type annotation is inferred automatically, transparent to the programmer.

If you feel adventurous, try out the pre-release continuations plugin for the Scala compiler yourself (available from the source repository). To learn all the gritty details about how continuations are implemented in Scala 2.8, have a look at the corresponding paper (accepted at ICFP'09).

thanks for providing the "glimpse". i agree with the previous comment, i find it very hard to grasp how the whole thing works. can you explain the timertask example? what is it doing exactly, and why is the result that no threads are used?

This makes about zero sense. I just spent some time on Wikipedia as well as a few other places to try to understand this. From my perspective, these are convoluted ways of adding numbers and I have no idea what is being gained or accomplished.

Is there an actual use for this construct that you can explain to demonstrate how this makes code easier to read/write/maintain?

Continuations are very useful implementing non-blocking operations, but making them look to end users as blocking operations. They work very well in those situations when capturing the rest of the computation is useful.

For example,in a GUI application, processing a large file in the event dispatcher thread will make the app freeze while it is processing the file.

You typically have two options here:

send the processing to a background thread, and that thread notifies the UI when it is done (using blocking operations)

Use asynchronous I/O

The first approach keeps the I/O code toghether and readable, at the cost of explicit threading and probably a performance hit (this last point is arguable).

The second approach will not add more threads, but the code, in general will be uglier (for example, you cannot have loops, etc.)

With continuations, you could - in theory - implement a facade on top of the asynchronus I/O operations that looks like blocking I/O. Using this facade, you could even write loops that at runtime will never block the UI :)

I hope this helps, but continuations are the kind of thing that are difficult to explain, but obvious when you need them!

edit: typo corrected.

edit: the sleep sample is a case where the sleep function, actually does not block, but the end result is the same. It works because sleep captures it's continuation (that is the println("no threads") ) and runs that inside the timer task.

As a GUI developer, I'm interested in understanding this a little better. Are you saying that with continuations you would write code like the following:

// ... Some UI inititialization code on the EDTreset{ var data = backgroundProcess(getData) // Update components (on the EDT) based on the data}// Optionally some more UI initialization

where backgroundProcess would handle calling getData on a background thread and then invoke the continuation back on the EDT? Essentially the callback of the background process would be the remainder of the reset block. Does this capture the essence of the use case, or is there a different/better way?

When writing code, there is often a threshold to consider when deciding whether a procedural or event driven approach should be used. So, if you have the following code:

var result = sendRequest(request, timeout);

If sendRequest takes 4 milliseconds on average, you wouldn't think twice about it. If, on the other hand it takes 4 hours to complete, you'd probably be concerned about keeping a thread hanging around waiting for a result for that long. In that case you'd consider using a more event based approach which is often more difficult to read than that one line (because you'll need to send the message out asynchronously and have an event handler). With continuations, you can keep the procedural style of programming but not have to worry about keeping a thread waiting for the call to return.

If the continuation is serlializeable. You can keep the above approach and it can also survive restarts. That's pretty cool. That you could have some code that gets halfway through and then starts again where it left off despite the application restartarting (or the code moving to a different node in a grid etc.). Continuations should combine pretty well with scala actors.

I agree, a better example than any of the others I have ever seen, but I'm still having trouble wrapping my head around continuations. This example would be better with a detailed explanation at each case in the example of what is going on.

If continuations are ever going to be generally useful in Scala, they need to be explained a whole lot better - otherwise people will simply not use them, or use them incorrectly.

I do some continuation-passing style programming in the implementation of VeriFast, my program verifier prototype. It's written in O'Caml. It uses symbolic execution. The verification process forks at each conditional construct. Here's a simplified fragment:

By taking a continuation als the last argument, and by using a function application infix operator $., you can do sequential composition of CPS functions without an accumulation of braces or parentheses. Unfortunately, in Scala, lambda expressions cannot appear as infix operator arguments (without parentheses or braces), so you cannot do this in Scala.

If I understand correctly, the Scala version with shift-reset would look something like this:

Hello, I'm a miscellaneous software developer out in the world, and I must add my voice to those clamoring about how confusing this article is. :)

I think I would get it if I studied several of the examples at length. A previous commenter says, "Contionuations makes complex thing possible, but one cannot expect simple thing easy, an tool to be used with care"--suggesting that it's not even possible to explain continuations to a neophyte in a way that doesn't require lengthy study!

But I learned about continuations in C#, which has a yield keyword that works as in another commenter's pseudocode:

In that example, the task being accomplished through the use of continuations is indeed simple, and it's also easy to understand: It is the direct transformation of a "for" loop into something very similar to an Iterable. This is just the sort of thing I did in C#, and when I'm working in Java, it's something I often wish I could do. Here's the crux of it: If you're calculating a bunch of things in sequence, via a loop or really in any old way, you can (in C#, according to my limited understanding) use this "yield" mechanism to make your loop stop at each step, and return the last value it calculated (as if you'd broken the loop via a return statement), while the rest of the loop, which is yet to be run, is still invisibly remembered somehow, so that the next time you ask for a value, it can start up again... until it hits its next yield statement, at which time it returns its next value.

The simple way to transform a "for" loop into an iterable thing, in Java, is to make an ArrayList before you start your loop, and add the result of each loop iteration to the list. Sometimes this is great. But sometimes it's crazily stupid--for instance, when the number of iterations is very large and the result produced by each iteration takes up lots of memory. Then, you wouldn't want to calculate every value, and store it in memory, before starting your iteration. But you might still want to use the nice syntax for iterating over Iterables. A C#-like "yield" keyword would allow this easily. Study the examples above and you will probably figure out how to do a similar thing in Scala. :)

(I don't mean to say that this idea of interrupting a loop to iterate over its results from outside is the only important aspect or use of continuations--it's just the one that's easy for me to understand.)

I wanted to post an example of continuations used on Android because the Android API (which is in Java) relies so heavily on asynchronous callbacks.

Callbacks tend to be harder to understand than synchronously blocking code because they force you to split logically sequential code into two chunks of non-contiguous code -- the code that makes the request, and the callback code. Continuations make it possible to express those two chunks as one contiguous chunk, hopefully making the code easier to understand (once you get your head around continuations). So I'm thinking continuations will be very useful on Android.

The callback in this example involves Android's TextToSpeech class. When you create an instance of TextToSpeech, it is not yet initialized, so you provide a callback that is called when the TextToSpeech is actually initialized. (No comment.)

Before we get to the example itself, here's a method it will use to build the required callback/listener object from a bit of code that will be turn out to be provided by a continuation:

Same example with some added comments about the continuation and what is happening when:

reset{
shift{ (continuation:Unit=>Any) =>
textSpeaker = new TextToSpeech(..., asInitListener(continuation))
// Can't use textSpeaker here -- it may not be initialized yet.
}
// The following line of code will be the continuation
// that will be used to build the callback object.
// It will not get executed in sequence here;
// it will instead get executed when the callback object is called.
textSpeaker.speak("NOW textSpeaker is definitely initialized.", ...)
}
// Can't use textSpeaker here -- it may not be initialized yet.

I have to admit the above code is still hard for me to understand. However, if I'm interpreting this article correctly, reset and shift should be thought of as primitives that can be used in higher-level constructs that are more tailored to specific use cases.

Also, part of what makes this code awkward is interfacing with a Java API. Imagine if Android had a Scala API, taking advantage of Scala features like continuations wherever appropriate.

Based on this article, I expected foo to be a function that I could invoke inside reset, but it seems I need to both define and invoke foo within the scope of the same reset. What am I missing or misunderstanding? Thanks, Topher.

we are planning to use continuation support in the scala language through shift and reset to track the users who are logged in(We are trying to use this instead of using session or cookie to track the users logged in). So please provide us any sample programe to implement the continuation support through shift and reset to track the logged users in scala.