处理程序控制堆栈框架如何在 Koka 中相对放置?
How handler control stackframes are placed relative to each other in Koka?
我这里有一个 Koka 片段,我希望有人能解释调用处理程序时堆栈帧会发生什么。我试图通过打印值和全局计数器使处理程序堆栈帧也可见,并且我已经在表达式时脱糖。
effect foo<a> { control foo() : a }
fun main() {
var c:int := 0
val r = (handler {
return(x:int) { println("in RET ctrl: " ++ x.show); x*2 }
control foo() {
c := c + 1
val this_c:int = c
println("in FOO ctrl_1. c is: " ++ c.show)
val r1 = resume(3)
println("in FOO ctrl_2, r1: " ++ r1.show ++ " this_c is: " ++ this_c.show)
r1*3
}
})(fn(){
println("throw first foo")
val first:int = foo()
println("throw second foo, first: " ++ first.show)
val snd:int = foo()
println("got second: " ++ snd.show ++ " RET SUM: " ++ (first + snd).show)
(first + snd)
})
println(r)
}
结果如下:
throw first foo
in FOO ctrl_1. c is: 1
throw second foo, first: 3
in FOO ctrl_1. c is: 2
got second: 3 RET SUM: 6
in RET ctrl: 6
in FOO ctrl_2, r1: 12 this_c is: 2
in FOO ctrl_2, r1: 36 this_c is: 1
108
两个“FOO_CTRL”处理程序帧现在如何位于原始 FN() 调用和 RET 处理程序之下?
我不确定这是否是通常用于 Koka 的术语,但根据我对定界延续的一般知识,这里有一个答案:
第一次调用 foo()
时,它可以访问延续(称之为 k1
):
val first:int = HOLE
println("throw second foo, first: " ++ first.show)
val snd:int = foo()
println("got second: " ++ snd.show ++ " RET SUM: " ++ (first + snd).show)
(first + snd)
请注意,如果 foo()
没有调用此延续,它就会消失。就堆栈帧而言,该帧不再在调用堆栈上;它被具体化为一个函数。调用堆栈如下所示:
foo (resume=k1)
<handle frame>
main
但是第一个 foo()
实际上调用了这个延续:resume(3)
。现在堆栈看起来像这样:
k1
foo (resume=k1)
<handle frame>
main
k1
最终再次调用 foo
。它的参数延续(称之为 k2
)以 val snd:int = HOLE
开始。但是第一个foo
又建立了提示,这样调用栈就变成了:
foo (resume=k2)
foo (resume=k1)
<handle frame>
main
然后第二个 foo
调用 resume
(=k2
).
当第一个foo
调用resume
时,它首先插入一个新的效果提示,这样当效果被调用时,调用堆栈中被捕获和移除的部分结束于上面现有的 foo
框架,而不是 <handle frame>
。在 foo
处理程序之前建立的不同效果的处理程序不会匹配已建立的提示,因此会捕获并删除堆栈上 foo
的每个实例。
不幸的是,尽我所能,我还没有找到明确的来源说明,在可用于多提示分隔延续的各种可能的控制操作中,Koka 的 resume
是建立在调用捕获的定界延续之前的新提示。我只能从你指出的行为中推断出来。
我这里有一个 Koka 片段,我希望有人能解释调用处理程序时堆栈帧会发生什么。我试图通过打印值和全局计数器使处理程序堆栈帧也可见,并且我已经在表达式时脱糖。
effect foo<a> { control foo() : a }
fun main() {
var c:int := 0
val r = (handler {
return(x:int) { println("in RET ctrl: " ++ x.show); x*2 }
control foo() {
c := c + 1
val this_c:int = c
println("in FOO ctrl_1. c is: " ++ c.show)
val r1 = resume(3)
println("in FOO ctrl_2, r1: " ++ r1.show ++ " this_c is: " ++ this_c.show)
r1*3
}
})(fn(){
println("throw first foo")
val first:int = foo()
println("throw second foo, first: " ++ first.show)
val snd:int = foo()
println("got second: " ++ snd.show ++ " RET SUM: " ++ (first + snd).show)
(first + snd)
})
println(r)
}
结果如下:
throw first foo
in FOO ctrl_1. c is: 1
throw second foo, first: 3
in FOO ctrl_1. c is: 2
got second: 3 RET SUM: 6
in RET ctrl: 6
in FOO ctrl_2, r1: 12 this_c is: 2
in FOO ctrl_2, r1: 36 this_c is: 1
108
两个“FOO_CTRL”处理程序帧现在如何位于原始 FN() 调用和 RET 处理程序之下?
我不确定这是否是通常用于 Koka 的术语,但根据我对定界延续的一般知识,这里有一个答案:
第一次调用 foo()
时,它可以访问延续(称之为 k1
):
val first:int = HOLE
println("throw second foo, first: " ++ first.show)
val snd:int = foo()
println("got second: " ++ snd.show ++ " RET SUM: " ++ (first + snd).show)
(first + snd)
请注意,如果 foo()
没有调用此延续,它就会消失。就堆栈帧而言,该帧不再在调用堆栈上;它被具体化为一个函数。调用堆栈如下所示:
foo (resume=k1)
<handle frame>
main
但是第一个 foo()
实际上调用了这个延续:resume(3)
。现在堆栈看起来像这样:
k1
foo (resume=k1)
<handle frame>
main
k1
最终再次调用 foo
。它的参数延续(称之为 k2
)以 val snd:int = HOLE
开始。但是第一个foo
又建立了提示,这样调用栈就变成了:
foo (resume=k2)
foo (resume=k1)
<handle frame>
main
然后第二个 foo
调用 resume
(=k2
).
当第一个foo
调用resume
时,它首先插入一个新的效果提示,这样当效果被调用时,调用堆栈中被捕获和移除的部分结束于上面现有的 foo
框架,而不是 <handle frame>
。在 foo
处理程序之前建立的不同效果的处理程序不会匹配已建立的提示,因此会捕获并删除堆栈上 foo
的每个实例。
不幸的是,尽我所能,我还没有找到明确的来源说明,在可用于多提示分隔延续的各种可能的控制操作中,Koka 的 resume
是建立在调用捕获的定界延续之前的新提示。我只能从你指出的行为中推断出来。