创建延续的功能
如果在分隔 reset
块之外调用 shift
,它可以用于创建自己在 reset
块内创建延续的函数。值得注意的是,shift
的类型不仅仅是 (((A => B) => C) => A)
,它实际上是 (((A => B) => C) => (A @cpsParam[B, C]))
。该注释标记了需要 CPS 转换的位置。在没有 reset
的情况下调用 shift
的函数的返回类型会被该注释感染。
在 reset
区块内,A @cpsParam[B, C]
的值似乎具有 A
的值,但实际上它只是假装。完成计算所需的延续类型为 A => B
,因此返回此类型的方法后面的代码必须返回 B
。C
是真实返回类型,在 CPS 转换后,函数调用的类型为 C
。
现在,这个例子取自库的 Scaladoc
val sessions = new HashMap[UUID, Int=>Unit]
def ask(prompt: String): Int @suspendable = // alias for @cpsParam[Unit, Unit]. @cps[Unit] is also an alias. (@cps[A] = @cpsParam[A,A])
shift {
k: (Int => Unit) => {
println(prompt)
val id = uuidGen
sessions += id -> k
}
}
def go(): Unit = reset {
println("Welcome!")
val first = ask("Please give me a number") // Uses CPS just like shift
val second = ask("Please enter another number")
printf("The sum of your numbers is: %d\n", first + second)
}
这里,ask
会将延续存储到地图中,稍后其他一些代码可以检索该会话并将查询结果传递给用户。通过这种方式,go
实际上可以使用异步库,而其代码看起来像普通的命令式代码。