什么是Scala延续,为什么要使用它们?
我刚刚完成,我一直在研究Scala 2.7和2.8之间的变化。最重要的一个似乎是continuations插件,但我不明白它对什么有用,或者它是如何工作的。我已经看到它对异步I/O有好处,但我还没有找到原因。关于该主题的一些比较流行的资源如下:什么是Scala延续,为什么要使用它们?,scala,scala-2.8,continuations,delimited-continuations,Scala,Scala 2.8,Continuations,Delimited Continuations,我刚刚完成,我一直在研究Scala 2.7和2.8之间的变化。最重要的一个似乎是continuations插件,但我不明白它对什么有用,或者它是如何工作的。我已经看到它对异步I/O有好处,但我还没有找到原因。关于该主题的一些比较流行的资源如下: 关于StackOverflow的问题: 不幸的是,这些引用中没有一个试图定义continuations的用途或者shift/reset函数应该做什么,我也没有找到任何可以这样做的引用。我无法猜到链接文章中的任何示例是如何工作的(或者它们
reset {
...
shift { k: (Int=>Int) => // The continuation k will be the '_ + 1' below.
k(7)
} + 1
}
// Result: 8
为什么结果是8?这可能会帮助我开始。Continuation捕获计算状态,稍后调用 考虑将移位表达式保留与将重置表达式保留为函数之间的计算。在移位表达式中,该函数称为k,它是连续函数。您可以传递它,稍后调用它,甚至不止一次 我认为重置表达式返回的值是=>之后移位表达式中表达式的值,但是关于这一点我不太确定 因此,使用continuations,您可以在函数中封装一段相当任意且非局部的代码。这可用于实现非标准控制流,例如协同路由或回溯 因此,应该在系统级别上使用延续。在应用程序代码中散布它们肯定会引起噩梦,比使用goto的最糟糕的意大利面代码糟糕得多 免责声明:我对Scala中的continuations没有深入的了解,我只是通过查看示例和了解Scheme中的continuations推断出来的。My确实解释了
reset
和shift
所做的事情,因此您可能需要再次阅读
另一个好的来源,我也在我的博客中指出,是维基百科上的条目。到目前为止,这个问题是最清楚的,尽管它没有使用Scala语法,并且显式地传递了continuation
我在我的博客中链接到了一篇关于分隔连续体的文章,但它似乎已经被破坏了。这篇文章给出了许多使用分隔连续体的例子
但我认为分隔连续概念的最好例子是Scala Swarm。在其中,库在某一点停止代码的执行,剩余的计算成为继续。然后库执行一些操作——在本例中,将计算传输到另一台主机,并将结果(访问的变量的值)返回到停止的计算
现在,你甚至不理解Scala页面上的简单示例,所以请阅读我的博客。在这篇文章中,我只关心解释这些基础知识,以及为什么结果是
8
我发现现有的解释在解释这个概念方面不如我所希望的有效。我希望这是清楚的(和正确的)。我还没有使用连续体
调用延续函数cf时:
shift
块的其余部分,并在其末尾再次开始
- 传递给
的参数是cf
块在继续执行时“计算”的参数。每次调用shift
cf
重置
块结束(或者如果没有块,则直到调用重置
)
块的结果(或reset
()参数,如果没有块)是reset
返回的结果cf
cf
之后继续,直到shift
块结束重置
块结束(或调用重置?)reset {
// A
shift { cf: (Int=>Int) =>
// B
val eleven = cf(10)
// E
println(eleven)
val oneHundredOne = cf(100)
// H
println(oneHundredOne)
oneHundredOne
}
// C execution continues here with the 10 as the context
// F execution continues here with 100
+ 1
// D 10.+(1) has been executed - 11 is returned from cf which gets assigned to eleven
// G 100.+(1) has been executed and 101 is returned and assigned to oneHundredOne
}
// I
这张照片是:
11
101
0 2 4 6 8 10
给出了for Scala的定界连续体的规范示例,对其进行了轻微修改,使
shift
的函数输入被命名为f
,因此不再是匿名的
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
Scala插件将此示例转换为从每次shift
到调用reset
的计算(在reset
的输入参数内)替换为输入到shift
的函数(例如f
)
被替换的计算被转移(即移动)到函数k
。函数f
输入函数k
,其中k
包含替换的计算,k
输入x:Int
,k
中的计算将移位(f)
替换为x
f(k) * 2
def k(x: Int): Int = x + 1
其作用与:
k(k(k(7))) * 2
def k(x: Int): Int = x + 1
注:输入参数x
的类型Int
(即k
的类型签名)由输入参数f
的类型签名给出
另一个概念上等价抽象的例子,即read
,是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)
}
我相信这将被转化为逻辑等价物:
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)
}
}
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)
}
}
}
我希望这能阐明连贯的共同抽象,这在前面介绍这两个例子时有些模糊。例如,规范的第一个示例在中以匿名函数的形式出现,而不是我命名的f
,因此一些读者并不清楚它在抽象上与第二个示例中的read
类似
因此,分隔的连续体产生了一种错觉
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")
}
A
B
D
E
G
F
C
def from0to10() = shift { (cont: Int => Unit) =>
for ( i <- 0 to 10 ) {
cont(i)
}
}
reset {
val x = from0to10()
print(s"$x ")
}
println()
0 1 2 3 4 5 6 7 8 9 10
reset {
print(s"${from0to10()} ")
}
println()
reset {
print(s"(${from0to10()},${from0to10()}) ")
}
println()
(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9) (0,10) (1,0) (1,1) (1,2) (1,3) (1,4) (1,5) (1,6) (1,7) (1,8) (1,9) (1,10) (2,0) (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (2,7) (2,8) (2,9) (2,10) (3,0) (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) (3,7) (3,8) (3,9) (3,10) (4,0) (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) (4,7) (4,8) (4,9) (4,10) (5,0) (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7) (5,8) (5,9) (5,10) (6,0) (6,1) (6,2) (6,3) (6,4) (6,5) (6,6) (6,7) (6,8) (6,9) (6,10) (7,0) (7,1) (7,2) (7,3) (7,4) (7,5) (7,6) (7,7) (7,8) (7,9) (7,10) (8,0) (8,1) (8,2) (8,3) (8,4) (8,5) (8,6) (8,7) (8,8) (8,9) (8,10) (9,0) (9,1) (9,2) (9,3) (9,4) (9,5) (9,6) (9,7) (9,8) (9,9) (9,10) (10,0) (10,1) (10,2) (10,3) (10,4) (10,5) (10,6) (10,7) (10,8) (10,9) (10,10)
def from0to10() = shift { (cont: Int => Unit) =>
for ( i <- 0 to 10 ) {
cont(i) // call the continuation
}
}
def back(action: => Unit) = shift { (cont: Unit => Unit) =>
cont()
action
}
reset {
val i = from0to10()
back { println() }
val j = from0to10
print(f"${i*j}%4d ") // printf-like formatted i*j
}
0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10
0 2 4 6 8 10 12 14 16 18 20
0 3 6 9 12 15 18 21 24 27 30
0 4 8 12 16 20 24 28 32 36 40
0 5 10 15 20 25 30 35 40 45 50
0 6 12 18 24 30 36 42 48 54 60
0 7 14 21 28 35 42 49 56 63 70
0 8 16 24 32 40 48 56 64 72 80
0 9 18 27 36 45 54 63 72 81 90
0 10 20 30 40 50 60 70 80 90 100
def onEven(x:Int) = shift { (cont: Unit => Unit) =>
if ((x&1)==0) {
cont() // call continuation only for even numbers
}
}
reset {
back { println() }
val x = from0to10()
onEven(x)
print(s"$x ")
}
0 2 4 6 8 10
// fail: just discard the continuation, force control to return back
def fail() = shift { (cont: Unit => Unit) => }
// succ: does nothing (well, passes control to the continuation), but has a funny signature
def succ():Unit @cpsParam[Unit,Unit] = { }
// def succ() = shift { (cont: Unit => Unit) => cont() }
reset {
back { println() }
val x = from0to10()
if ((x&1)==0) {
succ()
} else {
fail()
}
print(s"$x ")
}
0 2 4 6 8 10
def onTrue(b:Boolean) = {
if(!b) {
fail()
}
}
reset {
back { println() }
val x = from0to10()
onTrue ((x&1)==0)
print(s"$x ")
}
0 2 4 6 8 10
// negation: the hard way
class ControlTransferException extends Exception {}
def onOdd(x:Int) = shift { (cont: Unit => Unit) =>
try {
reset {
onEven(x)
throw new ControlTransferException() // return is not allowed here
}
cont()
} catch {
case e: ControlTransferException =>
case t: Throwable => throw t
}
}
reset {
back { println() }
val x = from0to10()
onOdd(x)
print(s"$x ")
}
1 3 5 7 9