1

我在这里有一个以下 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 处理程序下方?

4

1 回答 1

1

我不确定这是否是 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堆栈上的每个实例。

不幸的是,尽我所能尝试,我还没有找到明确的来源,说明在可能用于多提示分隔延续的各种可能的控制操作中,Kokaresume是在调用捕获的分隔延续之前建立新提示的操作。我只能从你指出的行为中推断出来。

于 2021-10-02T23:16:13.633 回答