创建延续的功能

如果在分隔 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,因此返回此类型的方法后面的代码必须返回 BC真实返回类型,在 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 实际上可以使用异步库,而其代码看起来像普通的命令式代码。