问题标签 [delimited-continuations]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
scheme - 嵌套分隔的延续转换
我试图理解分隔的延续,我正在阅读这篇文章:
http://community.schemewiki.org/?composable-continuations-tutorial
我发现了这个重置/移位转换
例如,我尝试了这个表达式的转换(我认为append-map来自 Racket)
得到了这个
结果相同'(1 4)
我想知道是否可以将相同类型的转换(将消除重置/移位)应用于这样的表达式:
以及结果将如何(它评估为'(4 5 5 6)
)。
haskell - 如何仅用纯函数表示定界延续?
我完成了 Oleg 的定界延续教程:
由于延续基本上是函数并且reset
/shift
甚至不是 monad api 的一部分,我想知道如何在没有newtype
and monad 机制的情况下实现分隔延续。
到目前为止,这是我想出的:
我很确定这是完全错误的,如果不是,我不知道如何正确应用运算符:
javascript - 如何实现基于多镜头定界延续的协程?
reset
我最近在 CPS 中使用/实现了定界延续shift
:
研究理论,我意识到以下联系:
据我了解的理论,Javascript 的生成器是非对称的、无堆栈的、一次性的和一流的协程。
- 不对称意味着被调用的生成器只能屈服于它的调用者
- 无堆栈意味着生成器不能从嵌套函数中产生
- one-shot 意味着发电机只能从特定位置恢复一次
- 第一类意味着生成器对象可以像普通数据一样传递
现在我想实现一个基于reset
/shift
具有以下特征的协程:
- 不对称
- 堆叠的
- 多发
- 头等舱
在查看以下人为示例时
似乎reset
/shift
已经满足最后两个标准,因为分隔的延续只是一流的可组合函数,我可以k
根据需要经常调用延续。为了回答原因,我想绕过与 list monad 相关的以下限制。这些假设是否正确?
即使到目前为止我还没有犯任何错误,我现在也对任务的复杂性感到不知所措。我不知道如何实现所需的协程,甚至不知道从哪里开始。我也没有找到示例实现。我不期望一个完整的实施,但也许一些指导来实现我的目标。
目标
我想绕过 Javascript 的生成器实现的协程的以下限制:
javascript - 如何从多个嵌套函数中丢弃定界延续?
我研究了定界延续,目前正在尝试丢弃它们以获得类似于引发异常的效果。
这是给我带来麻烦的原因:
似乎我只能将堆栈展开一帧。这是由设计shift
/reset
还是由我的实施中的缺陷引起的?
[编辑]
我让它在Haskell中工作,即这是一个实现问题:
raku - Raku(do) 所依赖的延续的具体细节是什么?
在 1990 年代和 2000 年代,编程语言爱好者几乎没有讨论过定界延续的话题。它最近重新成为编程语言讨论中的重要内容。
我希望有人至少可以权威地说明 Rakudo 的延续(与 Raku 相比)是否具有下面列出的六个特征中的每一个。我多说一点关于列表之后我希望得到的那种答案。
从在线消息[1]中逐字引用(带有格式修饰),该消息由推动向 JVM 添加延续工作的人员编写:
非对称:当延续暂停或产生时,执行返回到调用者(的
Continuation.run()
)。对称延续没有调用者的概念。当他们屈服时,他们必须指定另一个延续来将执行转移到。对称延拓和不对称延拓都没有比另一个更强大,并且每个都可以用来模拟另一个。Stackful:延续可以在调用堆栈中的任何深度暂停,而不是在同一子例程中,当延续为无堆栈时(如 C# 中的情况),定界上下文开始。即延续有它自己的堆栈而不是一个单一的子程序帧。堆叠延续比无堆叠延续更强大。
Delimited:延续捕获以特定调用开始的执行上下文(在我们的例子中,是某个可运行的主体),而不是整个执行状态一直到
main()
. 定界延续严格来说比无定界延续更强大(http://okmij.org/ftp/continuations/undelimited.html),后者被认为“没有实际用处”(http://okmij.org/ftp/continuations/against-调用cc.html)。多提示:可以嵌套延续,调用堆栈中的任何位置,任何封闭的延续都可以暂停。这类似于 try/catch 块的嵌套,并抛出某种类型的异常,将堆栈展开到处理它的最近的 catch 而不仅仅是最近的 catch。嵌套延续的一个示例可以是在虚拟线程中使用类似 Python 的生成器。生成器代码可以执行阻塞 IO 调用,这将暂停封闭线程的继续,而不仅仅是生成器:https ://youtu.be/9vupFNsND6o?t=2188
One-shot/non-reentrant:每次我们继续一个暂停的延续时,它的状态都会发生变化,我们不能多次从同一个暂停状态继续它(即我们不能及时返回)。这与可重入延续不同,每次我们挂起它们时,都会返回一个表示特定挂起点的新的不可变延续对象。即延续是一个时间点,每次我们继续它时,我们都会回到那个状态。可重入的延续比不可重入的延续更强大;即,他们可以做一些仅靠一次性延续绝对不可能的事情。
可克隆:如果我们能够克隆一次性延续,我们可以提供与可重入延续相同的能力。即使每次我们继续它都会改变延续,我们可以在继续创建该时间点的快照之前克隆它的状态,以便稍后返回。
Aiui 延续没有直接暴露在 Raku 中,所以与 Raku 相关的正确答案(相对于 Rakudo)可能是“没有延续”。但这对我来说并不清楚,所以在下面,如果我很幸运,我会在其中描述我希望答案中的内容,我会假装在两个 Raku 的背景下谈论它们是有意义的和乐道作为两个不同的领域。
这是我想象的可能的答案(尽管我只是有点疯狂地猜测什么是真的):
“作为“100 年”的语言设计,Raku当前的底层语义 [执行?] 模型至少需要无堆栈的一次性多提示分隔的延续。
从理论上的观点来看,Raku 的设计永远不能扩展到要求延续是可克隆的,但理论上它可以扩展到要求它们是可堆叠的。
Rakudo 实现了当前需要的延续语义。
MoarVM 内置了对这些语义的支持,并且如果 Raku 的设计如此扩展,则可以实际跟踪理论上可能的需求扩展。
JVM 和 JS 后端有合适的 shims 来实现相同的目标,尽管会以性能为代价。JVM 后端可以切换到使用 JVM 原生的 continuation 似乎是合理的,如果它得到它们,当然前提是它们满足要求,但我目前的印象是它可能实际上可能是十年在我们需要考虑过那座桥之前,我们必须离开,或者更多。”
(或类似的东西。)
如果答案还提供了有关上述内容的更多详细信息,也许是一些代码链接,那将是一个特别棒的补充。
类似地,如果答案包括几个简短的例子,说明这种持续能力如何在当前 Raku 功能中出现,以及关于它可能如何在某一天(比如 10 年后)出现在其他功能中的猜测,那么答案就会变得过于——最出色的一个。
PS。感谢@Larry,他对事物的理解足够深入,知道延续需要成为图片的一部分;感谢 Stefan O'Rear 的贡献,包括我认为一次性多提示分隔延续的初步实现;并感谢 jnthn 让梦想成真。
脚注
1将延续作为第一类构造引入 JVM 的工作正在进行中。这一努力的主要推动者是 Ron Pressler。以上是基于他在 11 月写的一条消息。
scheme - `prompt/control` 和 `shift/reset` 的区别示例
我不确定我是否理解分隔的延续运算符对prompt/control
和reset/shift
. 我了解一些基本的使用示例,但在这些示例中,它们的行为是相同的。
我在Dariusz Biernacki 和 Olivier Danvy的“关于分隔连续的动态范围”中找到了这个例子:
racket/control
我已将其翻译成 Scheme 并使用库在 Racket 中以预期结果成功运行:
他们的解释是,
在第一种情况下,当
k
被应用时,表达式在可以在功能上表示为的上下文中以及在可以表示为的元上下文中shift (fn kk => 1)
进行评估;此上下文被捕获并丢弃,中间答案是;这个中间答案从元上下文插入到顶层上下文中,即 应用于; 下一个中间答案是 ;这是最终的答案,因为元上下文是空的。fn v => 100 + v
(fn v => 10 + v) :: nil
1
fn v => 10 + v
1
11
在第二种情况下,当
k
被应用时,表达式控件 在由组合和(因此可以在功能上表示为)产生的上下文中以及在为空的元上下文中(fn kk => 1)
进行评估 ;此上下文被捕获并丢弃,中间答案是;这是最终的答案,因为元上下文是空的。fn v => 10 + v
fn v => 100 + v
fn v => 10 + (100 + v)
1
我对“元上下文”的想法感到困惑,他们将其定义为
直观地说,评估上下文表示直到
最近的封闭分隔符的其余计算,而元上下文表示所有剩余的计算。
我在这里没有得到“所有剩余计算”的想法,我不确定为什么会出现(fn v => 10 + v) :: nil
在第一个示例中(为什么是那段代码?)
我想知道是否还有更多的例子,可能有更多的细节,说明这两对操作符之间的差异,可能没有太多使用形式语义,这真的让我头疼。
编辑:我还看到两个shift
被包围的表达式的顺序确实有所不同:如果我交换它们,那么结果1
对于两者都是control
and reset
。
delimited-continuations - 处理程序控制堆栈帧如何在 Koka 中相对放置?
我在这里有一个以下 Koka 片段,我希望有人解释调用处理程序时堆栈帧会发生什么。我试图通过打印值和全局计数器来使处理程序堆栈帧也可见,并且在表达时我已经脱糖。
结果如下:
两个“FOO_CTRL”处理程序框架现在如何位于原始 FN() 调用以及 RET 处理程序下方?
haskell - 现代延续运算符到底是什么?
回到那天,我以为我明白了call/cc
。这些天来,我看到更多关于“定界”延续运算符的引用,它们似乎成对出现,例如shift
/ reset
,prompt
/ control
,有时甚至更奇特。但是我还没有看到任何基础知识的明确解释,所以
- 他们在做什么?
- 它们是干什么用的?
- 什么可能使一组运算符比另一种更适合特定语言/上下文/目的?