Scala Continuations Revisited


2011-11-28 Scala Continuations Revisited

The continuation is all about suspend and resume semantic, the idea is simple, powerful, but hard to get, especially when you try to rewind your mind from threading world.

After Scala2.8, a kind of continuation is introduced, which is called delimited continuation, aka. partial continuation or composable continuation, I had tried to understand scala’s continuation support well before, but didn’t get too much, So today, I try to do same thing again, here are some of the information that I regroup from former collections or new sources, I don’t hope to understand it this time either, but at least I leave some footprint. :-)

To understand Continuation and CPS concepts well, Jim Mcbeath’s blog post on Delimited Continuation is highly recommended to read!!!

1 Classification Of Continuation

  1. Full Continuation / First-Class Continuation
  2. Delimited Continuations / Partial Continuation / Composable Continuation

2 How to enable scala’s continuation support?

  1. enable compiler plugin
    • with compiler, scalac -P:continuations:enable
    • with REPL, scala -P:continuations:enable
  2. import continuation library
    • import scala.util.continuations._

3 Scala Continuation初瞥

        // 1. do something
        // 2. execution the continuation captured.

reset will setup/demarcate the boundary of the delimited continuation while shift will capture the delimited continuation. In above code snippet, continuationRef argument of shift is the captured DC which in fact is the continuationBody() part of the code.

注意: continuation指的是reset界定中shift后面剩余的部分, 这里很容易让expression和statement搞糊涂, 比如:
reset {
    shift { k1: (Unit=>Unit) =>
    shift { k2: (Unit=>Unit) =>
reset {
  shift { k: (Int=>Int) =>
  } + 1

可能因为是代码写法的缘故,一开始很容易搞不清楚为什么第一个例子中println("A")不包含在continuation中并且自己单独先执行,而后面一个代码事例中却将开始的1+算在continuation中,其实差别就是expression跟statement,原则上来讲, 是自己没有真正理解continuation的概念和scala的程序结构,呵呵

There are thus three types associated with shift:

In the signature for shift, the above three types appear as A, B and C, respectively: shift [A, B, C] (fun: ((A) ⇒ B) ⇒ C): A @scala.util.continuations.cpsParam[B,C]

Here’s where those types appear: C = reset { ...; A = shift { k:(A=>B) => ...; C } ...; B }

4 yield or return



5 潜在的可以简化的编程场景

  1. doing asynchronous I/O using Java NIO
  2. using executors and thread pools
  3. handling cross-request control flow in web applications

6 multitasking with scala continuations

The key different with threading model is that multitasking with continuations is coorperitive while multitasking with threads is preemptive. So the key point to make multitasking with continuation works is we need to offer a custom scheduler for the continuations execution and coordination.

reset {
        cont:(()=>Unit) => {
            connect() // synchronous
            startDump() // synchronous
            scheduler.register(cont) // yield without execution immediately since the io might be not ready.


7 值得参考的项目

  1. Swarm
  2. Akka’s DataFlow and NIO actor implementations
  3. Jim McBeath的blog posts和github项目(Scoroutine & NIOServer)
  4. Loft -

8 一些模式与规则

  1. “All code inside the reset, minus the shift expressions, is turned into continuations”
  2. reset的返回值类型跟shift的返回值类型相同?! - “the value of the evaluated reset block is the value of the last expression in the shift block that gets executed within that reset block”
  3. The gist of CPS is that we don’t use return. Rather than calling a subroutine and having it return to us, as is the case in the normal Direct Style, we pass a continuation to the subroutine for it to execute when it is done.”
  4. “In every method where we call a subroutine using CPS, that call is always the very last thing in the method.”
  5. “Any method that includes shift must be marked as CPS, and any method that calls a CPS method must be marked as CPS, until you reach the enclosing reset call.”

9 Limitations

  1. Using return statements in a CPS function is unlikely to do what you expect, and may cause type mismatch compiler errors, so you should not use them.
  2. When using an if statement, you may get an error like this: Foo.scala:21: error: then and else parts must both be cps code or neither of them
  3. The compiler plugin does not handle try blocks, so you can’t catch exceptions within CPS code. Those exceptions will be propagated out to the enclosing reset block and can be caught there - unless the continuation is suspended and executed later, in which case any exceptions would be propagated to the reset block of the code doing that later execution.
  4. You need to be careful when using looping constructs. As Tiark says, Capturing delimited continuations inside a while loop turns the loop basically into a general recursive function. You can follow the above link for details, but basically each invocation of shift within a looping construct allocates another stack frame, so after “looping” many times you will likely get a StackOverflowError.
  5. Some looping constructs can not be used with a shift inside them. To quote Tiark again: In a reset block you can do anything, but shifts are not allowed everywhere. The limitation is that everything on the call path between a shift and its enclosing reset must be "shift-aware". That rules out the regular foreach, map and filter methods because they know nothing about continuations, so they can't call closures containing shift.

10 Read More…

  1. My Question On Quora -
  2. highly recommended -
  4. Scala continuations and NIO meet JVM coroutines -
  5. Swarm Discussion On Lambda the Ultimate -

>>>>>> 更多阅读 <<<<<<

©王福强个人版权所有, All Rights Reserved.