I just finished Programming in Scala, and I\'ve been looking into the changes between Scala 2.7 and 2.8. The one that seems to be the most important is the continua
Given the canonical example from the research paper for Scala's delimited continuations, modified slightly so the function input to shift is given the name f and thus is no longer anonymous.
def f(k: Int => Int): Int = k(k(k(7)))
reset(
shift(f) + 1 // replace from here down with `f(k)` and move to `k`
) * 2
The Scala plugin transforms this example such that the computation (within the input argument of reset) starting from each shift to the invocation of reset is replaced with the function (e.g. f) input to shift.
The replaced computation is shifted (i.e. moved) into a function k. The function f inputs the function k, where k contains the replaced computation, k inputs x: Int, and the computation in k replaces shift(f) with x.
f(k) * 2
def k(x: Int): Int = x + 1
Which has the same effect as:
k(k(k(7))) * 2
def k(x: Int): Int = x + 1
Note the type Int of the input parameter x (i.e. the type signature of k) was given by the type signature of the input parameter of f.
Another borrowed example with the conceptually equivalent abstraction, i.e. read is the function input to shift:
def read(callback: Byte => Unit): Unit = myCallback = callback
reset {
val byte = "byte"
val byte1 = shift(read) // replace from here with `read(callback)` and move to `callback`
println(byte + "1 = " + byte1)
val byte2 = shift(read) // replace from here with `read(callback)` and move to `callback`
println(byte + "2 = " + byte2)
}
I believe this would be translated to the logical equivalent of:
val byte = "byte"
read(callback)
def callback(x: Byte): Unit {
val byte1 = x
println(byte + "1 = " + byte1)
read(callback2)
def callback2(x: Byte): Unit {
val byte2 = x
println(byte + "2 = " + byte1)
}
}
I hope this elucidates the coherent common abstraction which was somewhat obfuscated by prior presentation of these two examples. For example, the canonical first example was presented in the research paper as an anonymous function, instead of my named f, thus it was not immediately clear to some readers that it was abstractly analogous to the read in the borrowed second example.
Thus delimited continuations create the illusion of an inversion-of-control from "you call me from outside of reset" to "I call you inside reset".
Note the return type of f is, but k is not, required to be the same as the return type of reset, i.e. f has the freedom to declare any return type for k as long as f returns the same type as reset. Ditto for read and capture (see also ENV below).
Delimited continuations do not implicitly invert the control of state, e.g. read and callback are not pure functions. Thus the caller can not create referentially transparent expressions and thus does not have declarative (a.k.a. transparent) control over intended imperative semantics.
We can explicitly achieve pure functions with delimited continuations.
def aread(env: ENV): Tuple2[Byte,ENV] {
def read(callback: Tuple2[Byte,ENV] => ENV): ENV = env.myCallback(callback)
shift(read)
}
def pure(val env: ENV): ENV {
reset {
val (byte1, env) = aread(env)
val env = env.println("byte1 = " + byte1)
val (byte2, env) = aread(env)
val env = env.println("byte2 = " + byte2)
}
}
I believe this would be translated to the logical equivalent of:
def read(callback: Tuple2[Byte,ENV] => ENV, env: ENV): ENV =
env.myCallback(callback)
def pure(val env: ENV): ENV {
read(callback,env)
def callback(x: Tuple2[Byte,ENV]): ENV {
val (byte1, env) = x
val env = env.println("byte1 = " + byte1)
read(callback2,env)
def callback2(x: Tuple2[Byte,ENV]): ENV {
val (byte2, env) = x
val env = env.println("byte2 = " + byte2)
}
}
}
This is getting noisy, because of the explicit environment.
Tangentially note, Scala does not have Haskell's global type inference and thus as far as I know couldn't support implicit lifting to a state monad's unit (as one possible strategy for hiding the explicit environment), because Haskell's global (Hindley-Milner) type inference depends on not supporting diamond multiple virtual inheritance.