📜  Scala 中的延续

📅  最后修改于: 2022-05-13 01:55:29.325000             🧑  作者: Mango

Scala 中的延续

scala 中延续的主要思想是能够中断程序,保存其控制状态,并在稍后的时间点恢复它。延续作为一个概念出现在网络上,可以帮助基于事件的编程。延续,特别是定界延续,是一种通用的编程工具。最值得注意的是,我们对它们以受控方式挂起和恢复顺序代码路径的能力感兴趣,而无需语法开销并且不依赖于 VM 线程。
所以让我们试着用一个合适的例子来理解它。想象一个从网络返回一个字节的read()函数:

def read: Byte 

这通常是同步(阻塞)函数的签名。毕竟,它有一个返回值,并且在普通编程语言中,这意味着等待该值可用。连续读取两个字节并打印它们的程序如下所示:

问题在于,在 Web 浏览器或 node.js 或任何其他单线程、事件驱动的环境中,这是不可接受的。我们根本不能长时间阻塞,否则系统中不会发生任何其他事情。
这里的问题是我们必须以有趣的风格编写,即使使用 Scala 的轻量级闭包语法。还要注意每个回调通常如何导致新的缩进级别。一些程序员设法习惯了这种风格,但它并不能以一种非常自然的方式表示控制流,而且问题会随着程序的大小而增加。

我们注意到了 reset 和 shift 结构。这些术语对新手来说没有任何意义,但它们很久以前就在一篇学术论文中引入过,因此在 Scala 中被重用。基本上,重置界定了延续。有了完整的延续,整个程序的其余部分都将在延续的控制之下,但在这里,重置块之前和之后的任何内容都与延续无关。 (另外,reset 可以返回一个值,虽然这里我们不关心它。)

所以在这个例子中,按照从 A 到 Z 的字母

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

输出:

11
101

在上面的示例中,当调用延续函数cf时:

  1. 执行会跳过 shift 块的其余部分并在其末尾重新开始。传递给 cf 的参数是 shift 块在执行继续时“评估”的内容。对于cf的每次调用,这可能会有所不同。
  2. 执行一直持续到重置块结束(或者如果没有块,则直到调用重置)。重置块的结果(如果没有块,则为reset()的参数)是cf返回的结果。
  3. cf之后继续执行,直到shift块结束。
  4. 执行一直跳到重置块结束(或调用重置?)

让我们再看一个例子:

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