This page is no longer maintained — Please continue to the home page at www.scala-lang.org

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:

reset {
  ...
  shift { k: (Int=>Int) =>  // the continuation k will be the '_ + 1' below
    k(7)
  } + 1
}
// result: 8

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.

def foo(): Int @cps[Int,Int] = { // could leave out return type
  shift { k: (Int=>Int) =>
    k(7)
  } + 1
}
reset(2 * foo()) // result: 16

Here is another example that illustrates the connection to callback driven API's:

import java.util.{Timer,TimerTask}

val timer = new Timer()

def sleep(delay: Int) = shift { k: (Unit => Unit) =>
  timer.schedule(new TimerTask {
    def run() = k() // in a real program, we'd execute k on a thread pool
  }, delay)
}

reset {
  println("look, Ma ...")
  sleep(1000)
  println(" no threads!")
}

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).

 

 

Re: A Taste of 2.8: Continuations

Tiark, it seems continuations are too obvious to you for you to chose good examples. :-) From your paper, the following two examples are far superior in getting the point accross:

 

reset {
  shift { k: (Int=>Int) =>     k(k(k(7)))   } + 1 } * 2 // result 20 
and
def foo() = {
  1 + shift(k => k(k(k(7))))
}
def bar() = {
  foo() * 2
}
def baz() = {
  reset(bar())
}
baz()  // result 70
 

Though my favorite example is 4.2. Only I can't wrap my head around it yet.

 

Re: A Taste of 2.8: Continuations

hi,

 

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?

Re: A Taste of 2.8: Continuations

Me too !

I read about scala continuations few articles ... and to be honest I am still does not get it :).

Please provide more basic examples.

 

Thanks, Arthur.

No clue

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? 

 

Re: No clue

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:

  1. send the processing to a background thread, and that thread notifies the UI when it is done (using blocking operations)
  2. 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.

Re: No clue

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 EDT
reset
{
  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?

Re: A Taste of 2.8: Continuations

Similar to the previous repliers, i also don't quite understand what's the use of all this.

Beaten with this kind of uncurable ignorance, i ask myself:

 

What kinds of problems are solved by continuations?

 

All the examples on http://en.wikipedia.org/wiki/Continuation and http://en.wikipedia.org/wiki/Continuation-passing_style seem to present way more complex solutions for problems that can be solved quite simply.

 

I guess what is missing here, are real good examples for problems solved by continuations.

Re: A Taste of 2.8: Continuations

Many scary, advanced things are possible. See okmij.org/ftp/Computation/Continuations.html, so you brain can be painfully subject to them.

 

Re: A Taste of 2.8: Continuations

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.

Re: A Taste of 2.8: Continuations

Hi,

maybe somebody can post very simple example using println( "hello1" ), println( "hello2" ) ... to see how control flow goes ?

Thanks, Arthur.

Re: A Taste of 2.8: Continuations

reset {
    println(1) 
} 
//prints: 1

reset {
    println(1)
    shift { cont => }
    println(2)
}
//prints: 1

reset {
    println(1)
    shift { cont =>
        println(2)
    }
    println(3)
}
//prints: 1 2

reset {
    println(1)
    shift { cont =>
        cont()
        println(2)
    }
    println(3)
}
//prints: 1 3 2

reset {
    println(1)
    shift { cont =>
        cont()
        cont()
        println(2)
    }
    println(3)
}
//prints: 1 3 3 2

Re: A Taste of 2.8: Continuations

A good example! Much easier to understand than the other ones.

Re: A Taste of 2.8: Continuations

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.

Daniel Sobral's explanation is by far the best I've seen yet.

Re: A Taste of 2.8: Continuations

Assume such use case (in scala-like pseudocode)

class My(array: Array[String]) {
    def next : String = for (s <- array) yield return s
}
val my = new My(Array("One", "Two", "Three"))
assert my.next == "One"
assert my.next == "Two"
assert my.next == "Three"

How this can be correctly formulated using Scala 2.8 continuation facility?

Re: A Taste of 2.8: Continuations

I think something like this might work:

class YieldReturnIterator[+T] extends Iterator[T] {
    private[this] var nextValue: Option[Option[T]] = None
    private[this] var getNext: () => Unit = null
    protected def yieldReturn(x: T): Unit @cps[Unit, Unit] = {
        shift { cont =>
            nextValue = Some(Some(x))
            getNext = cont
        }
    }
    protected def body: Unit @cps[Unit, Unit]

    {
        getNext = { () =>
            reset {
                body
                nextValue = Some(None)
                getNext = null
            }
        }
    }
    def hasNext: Boolean = {
        if (nextValue.isEmpty)
            getNext()
        nextValue.get.isDefined
    }            
    def next: String = {
        if (nextValue.isEmpty)
            getNext()
        val e = nextValue.get.get
        nextValue = None
        e
    }
}

class My(array: Array[String]) extends YieldReturnIterator[String] {
    def body = for (s <- array) yieldReturn(s)
}

I am assuming here that for statements work with continuations. The way to do this would probably be to have a CPS overload of foreach:

class Array[T] {
    def foreach[X](body: T => Unit @cps[X, X]): Unit @cps[X, X] = {
        def iter(i: Int): Unit @cps[X, X] = {
            if (i < this.length) {
                body(this(i))
                iter(i + 1)
            }
        }
        iter(0)
    }
}

Re: A Taste of 2.8: Continuations

Thanks, I'll try wrap my head arount it.

Contionuations makes complex thing possible, but one cannot expect simple thing easy, an tool to be used with care

Re: A Taste of 2.8: Continuations

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:

let ($.) f x = f x
let rec verify_stmt h s c k =
  match c with
  | Call (f, args) =>
    eval_exprs h s args $. fun h values =>
    let (params, pre, post) = lookup_function f in
    let s' = zip params values in
    consume h s' pre $. fun h s' =>
    produce h s' post $. fun h _ =>
    k h s
  | If (cond, c1, c2) =>
    eval h s cond $. fun h value =>
    assume value (fun () => verify_stmt h s c1 k);
    assume (Not value) (fun () => verify_stmt h s c2 k)
let verify_function f params pre post body =
  let s = map (fun x => (x, fresh_symbol x)) params in
  let h = [] in
  produce h s pre $. fun h s =>
  verify_stmt h s body $. fun h _ =>
  consume h s post $. fun _ _ =>
  ()

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:

def caseSplit[T](v: Term, branch1: => T, branch2: => T): T = {
  shift { k =>
    assume(v, k(branch1))
    assume(Not(v), k(branch2))
  }
}

def verifyStmt(h: Heap, s: Store, c: Command): (Heap, Store) = {
  c match {
    case Call(f, args) =>
      val (h0, values) = evalExprs(h, s, args)
      val (params, pre, post) = lookupFunction(f)
      val s0 = params zip values
      val (h1, s1) = consume(h0, s0, pre)
      val (h2, _) = produce(h1, s1, post)
      (h2, s)
    case If(cond, c1, c2) =>
      val (h0, value) = eval(h, s, cond)
      caseSplit(value, verifyStmt(h0, s, c1), verifyStmt(h0, s, c2))
  }
}

def verifyFunction(f: String, params: List[String], pre: Assertion, post: Assertion,
                   body: Command) {
  val s = params map (x => (x, freshSymbol(x)))
  val h = emptyHeap
  reset {
    val (h1, s1) = produce(h, s, pre)
    val (h2, _) = verifyStmt(h1, s1, body)
    consume(h2, s1, post)
  }
}

It's a bit of a pain that I can't redefine locals, but apart from that the Scala version is at least as clean as the O'Caml version.

Re: A Taste of 2.8: Continuations

Maybe someone could explain how continuations would be useful in combination with NIO through a simple example?

 

Re: A Taste of 2.8: Continuations

Exactly !!!

I am waiting for simple example, can be based on NIO but please remember that Selector is also very hard without continuations ;).

Maybe I/O exaple in pseudo-code with shift and reset ??

 

Thanks :))

Re: A Taste of 2.8: Continuations

Here's a minimal NIO app: a single-threaded server that accepts an arbitrary number of concurrent connections and that echoes back each byte it receives.

import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.{SelectionKey, Selector, ServerSocketChannel, SocketChannel}

val selector = Selector.open()
// Pseudo-blocking wrappers around the non-blocking NIO methods
def asRunnable(body: => Unit) = new Runnable { def run() { body() } }

def accept(s: ServerSocketChannel): SocketChannel @cps[Unit, Unit] =
    shift { cont =>
        selector.register(s, SelectionKey.OP_ACCEPT, asRunnable {
            val c = s.accept()
            c.configureBlocking(false)
            cont(c)
        })
    }

def read(s: SocketChannel, b: ByteBuffer): Int @cps[Unit, Unit] =
    shift { cont =>
        selector.register(s, SelectionKey.OP_READ, asRunnable { cont(s.read(b)) })
    }

def write(s: SocketChannel, b: ByteBuffer): Int @cps[Unit, Unit] =
    shift { cont =>
        selector.register(s, SelectionKey.OP_WRITE, asRunnable { cont(s.write(b)) })
    }
// The application, in blocking style

val server = ServerSocketChannel.open()
server.socket().bind(new InetSocketAddress(12345))
server.configureBlocking(false)

reset {
    while (true) {
        val socket = accept(server)
        reset { // A pseudo-fork
            val buffer = ByteBuffer.allocateDirect(1)
            def iter(): Unit @cps[Unit, Unit] = {
                buffer.clear()
                val count = read(socket, buffer)
                if (count < 1)
                    socket.close()
                else {
                    buffer.flip()
                    write(socket, buffer)
                    iter()
                }
            }
            iter()
        }
    }
}

// The NIO event pump

while (true) {
    selector.select()
    val l = selector.selectedKeys().toList
    selector.selectedKeys().clear()
    for (k <- l) {
        k.interestOps(0)
        k.attachment().asInstanceOf[Runnable].run()
    }
}

Disclaimer: I did not actually try to compile this.

Re: A Taste of 2.8: Continuations

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:

class My(array: Array[String]) {
    def next : String = for (s <- array) yield return s
}
val my = new My(Array("One", "Two", "Three"))
assert my.next == "One"
assert my.next == "Two"
assert my.next == "Three"

 

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.)

Re: A Taste of 2.8: Continuations


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:

  private def asInitListener(bodyOfListener:Unit=>Any) =
    new OnInitListener(){ override def onInit(ignored:Int){ bodyOfListener() } }


Now the example, a code fragment that creates a TextToSpeech and (later, after the TextToSpeech is initialized) uses it:

    reset{
      shift{ (continuation:Unit=>Any) =>
        textSpeaker = new TextToSpeech(..., asInitListener(continuation))
      }
      textSpeaker.speak("NOW textSpeaker is definitely initialized.", ...)
    }


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.

Mark

Re: A Taste of 2.8: Continuations

I think I am missing some small detail.  What is it?

$ scala -P:continuations:enable
Welcome to Scala version 2.8.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.
 
scala> import scala.util.continuations._                                     
import scala.util.continuations._
 
scala> def foo() = { shift { k: (Int => Int) => k(7) } }                     
<console>:2: error: type mismatch;
 found   : Int @scala.util.continuations.cpsSynth @scala.util.continuations.cpsParam[Int,Int]
 required: Int
object RequestResult$line2$object {
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.

 

A Taste of 2.8: Continuations

       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.

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland