Raku(do)所依赖的延续有哪些具体内容



在20世纪90年代,编程语言爱好者几乎没有讨论定界延续的话题。它最近重新成为编程语言讨论中的一个重要内容。

Aiui延续在Raku中没有直接暴露,所以也许与Raku相关的正确答案(与Rakudo相对)是";没有延续";。但是拉库多呢?Raku的哪些功能依赖于它们?

我希望有人至少能权威地说出Rakudo(与Raku相比)的延续是否具有下面列出的六个特征中的每一个,这六个特征基本上是逐字逐句地引用了Ron Pressler在2019年11月写的一篇评论,他是一个旨在向JVM添加延续的项目的负责人。

  • 不对称:当延续挂起或让步时,执行返回到(Continuation.run()的)调用者。对称延续没有调用者的概念。当它们屈服时,它们必须指定另一个延续来将执行转移到。对称延续和非对称延续都不比另一个更强大,并且每一个都可以用来模拟另一个。

  • Stackful:延续可以在调用堆栈的任何深度挂起,而不是在延续无堆栈时(如C#中的情况)分隔上下文开始的同一个子例程中。即延续有自己的堆栈,而不仅仅是一个子程序框架。有堆叠的延续比无堆叠的延续更强大。

  • 分隔:continuation捕获从特定调用(在我们的例子中,是某个可运行程序的主体)开始的执行上下文,而不是一直到main()的整个执行状态。严格意义上讲,限定的延续比未限定的延续更强大(http://okmij.org/ftp/continuations/undelimited.html),后者认为";实际上没有用";(http://okmij.org/ftp/continuations/against-callcc.html)。

  • 多提示:continuation可以嵌套,并且在调用堆栈的任何位置,任何封闭的continuation都可以挂起。这类似于try/catch块的嵌套,并抛出特定类型的异常,该异常将堆栈展开到处理它的最近的catch,而不仅仅是最近的catch。嵌套延续的一个例子可以是在虚拟线程中使用类似Python的生成器。生成器代码可以执行阻塞IO调用,这将挂起封闭线程的延续,而不仅仅是生成器:https://youtu.be/9vupFNsND6o?t=2188

  • 一次性/不可重入:每次我们继续一个暂停的延续,它的状态都会发生变化,我们不能多次从同一暂停状态延续它(即我们不能回到过去)。这与可重入延续不同,每次我们挂起它们时,都会返回一个表示特定挂接点的新的不可变延续对象。也就是说,延续是一个时间点,每次我们延续它,我们都会回到那个状态。严格意义上讲,重入延续比不重入延续更强大;也就是说,他们可以用一次连拍来做一些完全不可能的事情。

  • 可克隆:如果我们能够克隆一次性延续,我们就可以提供与可重入延续相同的能力。即使每次我们继续它时,延续都会发生变化,但我们可以在继续创建该时间点的快照之前克隆它的状态,以便稍后返回。


PS。感谢@Larry,他对事物有着深刻的理解,知道延续需要成为画面的一部分;感谢Stefan O'Rear的贡献,包括我认为的一次性多提示定界延续的最初实现;感谢jnthn让梦想成真。

Rakudo使用延续作为两个功能的实现策略:

  • gather/take-用于实现延迟迭代器
  • 使线程池上的await不阻塞

实现的延续的特性遵循这些语言特性的要求。我将以与上面略有不同的顺序来介绍它们,因为这样可以方便地进行解释。

  • Stackful-是的,因为我们需要能够在调用堆栈中相对于gather或线程池工作循环的任何深度执行takeawait。例如,您可以在遇到的每个节点的gathertake中编写递归图遍历算法。对于await,这是Raku的awaitawait之间差异的核心,正如在许多其他语言中看到的那样:您不必一直重构调用堆栈
  • 分隔-是。延续重置操作安装一个标记(或"提示"),当我们执行延续控制操作时,我们在这个分隔符处对堆栈进行切片。我无法想象如果没有对Raku特性进行定界,您将如何实现这些特性
  • 多提示-是的,这是必需的,因为您可以在另一个gather的实现中迭代由gather提供的一个数据源,或者在gather中执行await
  • 非对称-在执行完延续之后,在reset指令之后继续执行。在await的情况下,我们在工作任务队列中找到另一个任务,在take的情况下我们回到迭代器的pull-one方法,可以返回所取的值。我认为这种方法非常适合一种只有少数功能使用延续的语言
  • 一次性/不可重入-是的,至少在MoarVM中,运行时的内存安全性取决于此属性。它是由原子比较和交换操作强制执行的,因此如果两个线程竞相调用延续,那么只有一个线程可以成功。没有任何Raku特性需要可重入延续所暗示的额外复杂性
  • 可克隆-不,因为没有Raku功能需要它。理论上,在MoarVM中实现这一点并不太可怕,可以说";是的,我们可以做到";,但我怀疑这引发了很多问题,比如";克隆应该有多深";。如果您只是克隆了所有调用记录和类似的记录,那么您仍然可以在克隆之间共享Scalar容器、Array

据我所知,尽管我从远处了解,JVM的延续至少在一定程度上是针对Rakuawait机制所在的相同设计空间,因此,如果它们最终没有提供Raku所需的,我会感到惊讶。这将明显简化Raku代码到JVM的编译(目前它执行全局CPS转换,就像它执行代码生成一样,奇怪的是,它比我预期的要简单),而且它几乎肯定也会执行得更好,因为从JIT编译器的角度来看,所需的转换可能会掩盖很多事情。

就代码而言,您可以看到当前的continuations实现,它使用continuation数据结构,该结构又具有不同的内存管理位。在撰写本文时,作为正在进行的调度器工作所需的新调用堆栈表示的一部分,这些都经过了显著的重构;这些更改确实提高了继续操作的效率,但不会改变整个操作集。

最新更新