Open In App

Continuations in Scala

Improve
Improve
Like Article
Like
Save
Share
Report

The main idea of continuations in scala is the ability to interrupt a program, save its control state, and resume it at a later point in time. Continuations pop up on the web as a concept that could help with event-based programming. Continuations, and in particular delimited continuations, are a versatile programming tool. Most notably, we are interested in their ability to suspend and resume sequential code paths in a controlled way without the syntactic overhead and without being tied to VM threads.
So let’s try to understand it with a proper example. Imagine a read() function which returns a byte from the network:

def read: Byte 

This is typically the signature of a synchronous (blocking) function. After all, it has a return value and in normal programming languages, which means waiting for that value to be available. A program that reads two bytes in a row and prints them looks like this:

val byte1 = read
println(“byte1 = ” + byte1)
Val byte2 = read
println(“byte2 = ” + byte2)

The issue is that in a web browser or node.js or any other single-threaded, event-driven environment, this is not acceptable. we simply cannot block for a long time, otherwise, nothing else can happen in the system.
The issue here is that we must write in a funny style, even with Scala’s lightweight syntax for closures. Note also how each callback typically causes a new level of indentation. Some programmers manage to get used to this style, but it does not represent the control flow in a very natural way, and the issue grows with the size of the program.

import scala.util.continuations._
reset {
val byte1 = shift(read)
println(“byte1 = ” + byte1)
val byte2 = shift(read)
println(“byte2 = ” + byte2)}

We notice the reset and shift constructs. These terms don’t make any sense to a newcomer, but they have introduced a long time ago in an academic paper so are reused in Scala. Basically, reset delimits the continuation. With full continuations, the entire rest of the program would be under the control of the continuation, but here, whatever is before and after the reset block has nothing to do with continuations at all. (Also, reset can return a value, although here we don’t care about it.)

So in this example, follow the letters from A to Z




reset {
  // A
  shift { cf: (Int=>Int) =>
  
    // B
    val eleven = cf(10)
  
    // E
    println(eleven)
    val oneHundredOne = cf(100)
  
    // H
    println(oneHundredOne)
    oneHundredOne
  }
    
}


Output:

11
101

In above example when a continuation function cf is called:

  1. Execution skips over the rest of the shift block and begins again at the end of it. the parameter passed to cf is what the shift block “evaluates” to as execution continues. this can be different for every call to cf.
  2. Execution continues until the end of the reset block (or until a call to reset if there is no block). the result of the reset block (or the parameter to reset() if there is no block) is what cf returns.
  3. Execution continues after cf until the end of the shift block.
  4. Execution skips until the end of the reset block (or a call to reset?)

let’s see one more example:




reset {
    println("A")
    shift { k1: (Unit=>Unit) =>
        println("B")
        k1()
        println("C")
    }
    println("D")
    shift { k2: (Unit=>Unit) =>
        println("E")
        k2()
        println("F")
    }
    println("G")
}


Output:

A
B
D
E
G
F
C


Last Updated : 04 May, 2020
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads