什么是Scala延续,为什么要使用它们?

什么是Scala延续,为什么要使用它们?,scala,scala-2.8,continuations,delimited-continuations,Scala,Scala 2.8,Continuations,Delimited Continuations,我刚刚完成,我一直在研究Scala 2.7和2.8之间的变化。最重要的一个似乎是continuations插件,但我不明白它对什么有用,或者它是如何工作的。我已经看到它对异步I/O有好处,但我还没有找到原因。关于该主题的一些比较流行的资源如下: 关于StackOverflow的问题: 不幸的是,这些引用中没有一个试图定义continuations的用途或者shift/reset函数应该做什么,我也没有找到任何可以这样做的引用。我无法猜到链接文章中的任何示例是如何工作的(或者它们

我刚刚完成,我一直在研究Scala 2.7和2.8之间的变化。最重要的一个似乎是continuations插件,但我不明白它对什么有用,或者它是如何工作的。我已经看到它对异步I/O有好处,但我还没有找到原因。关于该主题的一些比较流行的资源如下:

关于StackOverflow的问题:

不幸的是,这些引用中没有一个试图定义continuations的用途或者shift/reset函数应该做什么,我也没有找到任何可以这样做的引用。我无法猜到链接文章中的任何示例是如何工作的(或者它们是做什么的),因此一种帮助我的方法是逐行浏览其中一个示例。即使是第三篇文章中的这个简单的例子:

reset {
    ...
    shift { k: (Int=>Int) =>  // The continuation k will be the '_ + 1' below.
        k(7)
    } + 1
}
// Result: 8

为什么结果是8?这可能会帮助我开始。Continuation捕获计算状态,稍后调用

考虑将移位表达式保留与将重置表达式保留为函数之间的计算。在移位表达式中,该函数称为k,它是连续函数。您可以传递它,稍后调用它,甚至不止一次

我认为重置表达式返回的值是=>之后移位表达式中表达式的值,但是关于这一点我不太确定

因此,使用continuations,您可以在函数中封装一段相当任意且非局部的代码。这可用于实现非标准控制流,例如协同路由或回溯

因此,应该在系统级别上使用延续。在应用程序代码中散布它们肯定会引起噩梦,比使用goto的最糟糕的意大利面代码糟糕得多

免责声明:我对Scala中的continuations没有深入的了解,我只是通过查看示例和了解Scheme中的continuations推断出来的。

My确实解释了
reset
shift
所做的事情,因此您可能需要再次阅读

另一个好的来源,我也在我的博客中指出,是维基百科上的条目。到目前为止,这个问题是最清楚的,尽管它没有使用Scala语法,并且显式地传递了continuation

我在我的博客中链接到了一篇关于分隔连续体的文章,但它似乎已经被破坏了。这篇文章给出了许多使用分隔连续体的例子

但我认为分隔连续概念的最好例子是Scala Swarm。在其中,库在某一点停止代码的执行,剩余的计算成为继续。然后库执行一些操作——在本例中,将计算传输到另一台主机,并将结果(访问的变量的值)返回到停止的计算


现在,你甚至不理解Scala页面上的简单示例,所以请阅读我的博客。在这篇文章中,我只关心解释这些基础知识,以及为什么结果是
8

我发现现有的解释在解释这个概念方面不如我所希望的有效。我希望这是清楚的(和正确的)。我还没有使用连续体

调用延续函数cf时:

  • 执行跳过
    shift
    块的其余部分,并在其末尾再次开始
    • 传递给
      cf
      的参数是
      shift
      块在继续执行时“计算”的参数。每次调用
      cf
  • 执行继续,直到
    重置
    块结束(或者如果没有块,则直到调用
    重置
    • reset
      块的结果(或
      reset
      ()参数,如果没有块)是
      cf
      返回的结果
  • 执行在
    cf
    之后继续,直到
    shift
    块结束
  • 执行跳过,直到
    重置
    块结束(或调用重置?)
  • 所以在这个例子中,按照字母A到Z

    reset {
      // A
      shift { cf: (Int=>Int) =>
        // B
        val eleven = cf(10)
        // E
        println(eleven)
        val oneHundredOne = cf(100)
        // H
        println(oneHundredOne)
        oneHundredOne
      }
      // C execution continues here with the 10 as the context
      // F execution continues here with 100
      + 1
      // D 10.+(1) has been executed - 11 is returned from cf which gets assigned to eleven
      // G 100.+(1) has been executed and 101 is returned and assigned to oneHundredOne
    }
    // I
    
    这张照片是:

    11
    101
    
    0 2 4 6 8 10 
    

    给出了for Scala的定界连续体的规范示例,对其进行了轻微修改,使
    shift
    的函数输入被命名为
    f
    ,因此不再是匿名的

    def f(k: Int => Int): Int = k(k(k(7)))
    reset(
      shift(f) + 1   // replace from here down with `f(k)` and move to `k`
    ) * 2
    
    Scala插件将此示例转换为从每次
    shift
    到调用
    reset
    的计算(在
    reset
    的输入参数内)替换为输入到
    shift
    的函数(例如
    f

    被替换的计算被转移(即移动)到函数
    k
    。函数
    f
    输入函数
    k
    ,其中
    k
    包含替换的计算,
    k
    输入
    x:Int
    k
    中的计算将
    移位(f)
    替换为
    x

    f(k) * 2
    def k(x: Int): Int = x + 1
    
    其作用与:

    k(k(k(7))) * 2
    def k(x: Int): Int = x + 1
    
    注:输入参数
    x
    的类型
    Int
    (即
    k
    的类型签名)由输入参数
    f
    的类型签名给出

    另一个概念上等价抽象的例子,即
    read
    ,是
    shift
    的函数输入:

    def read(callback: Byte => Unit): Unit = myCallback = callback
    reset {
      val byte = "byte"
    
      val byte1 = shift(read)   // replace from here with `read(callback)` and move to `callback`
      println(byte + "1 = " + byte1)
      val byte2 = shift(read)   // replace from here with `read(callback)` and move to `callback`
      println(byte + "2 = " + byte2)
    }
    
    我相信这将被转化为逻辑等价物:

    val byte = "byte"
    
    read(callback)
    def callback(x: Byte): Unit {
      val byte1 = x
      println(byte + "1 = " + byte1)
      read(callback2)
      def callback2(x: Byte): Unit {
        val byte2 = x
        println(byte + "2 = " + byte1)
      }
    }
    
    def read(callback: Tuple2[Byte,ENV] => ENV, env: ENV): ENV =
      env.myCallback(callback)
    def pure(val env: ENV): ENV {
      read(callback,env)
      def callback(x: Tuple2[Byte,ENV]): ENV {
        val (byte1, env) = x
        val env = env.println("byte1 = " + byte1)
        read(callback2,env)
        def callback2(x: Tuple2[Byte,ENV]): ENV {
          val (byte2, env) = x
          val env = env.println("byte2 = " + byte2)
        }
      }
    }
    
    我希望这能阐明连贯的共同抽象,这在前面介绍这两个例子时有些模糊。例如,规范的第一个示例在中以匿名函数的形式出现,而不是我命名的
    f
    ,因此一些读者并不清楚它在抽象上与第二个示例中的
    read
    类似

    因此,分隔的连续体产生了一种错觉
    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
    
    def from0to10() = shift { (cont: Int => Unit) =>
       for ( i <- 0 to 10 ) {
         cont(i)
       }
    }
    
    reset {
      val x = from0to10()
      print(s"$x ")
    }
    println()
    
    0 1 2 3 4 5 6 7 8 9 10 
    
    reset {
      print(s"${from0to10()} ")
    }
    println()
    
    reset {
      print(s"(${from0to10()},${from0to10()}) ")
    }
    println()
    
    (0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9) (0,10) (1,0) (1,1) (1,2) (1,3) (1,4) (1,5) (1,6) (1,7) (1,8) (1,9) (1,10) (2,0) (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (2,7) (2,8) (2,9) (2,10) (3,0) (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) (3,7) (3,8) (3,9) (3,10) (4,0) (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) (4,7) (4,8) (4,9) (4,10) (5,0) (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7) (5,8) (5,9) (5,10) (6,0) (6,1) (6,2) (6,3) (6,4) (6,5) (6,6) (6,7) (6,8) (6,9) (6,10) (7,0) (7,1) (7,2) (7,3) (7,4) (7,5) (7,6) (7,7) (7,8) (7,9) (7,10) (8,0) (8,1) (8,2) (8,3) (8,4) (8,5) (8,6) (8,7) (8,8) (8,9) (8,10) (9,0) (9,1) (9,2) (9,3) (9,4) (9,5) (9,6) (9,7) (9,8) (9,9) (9,10) (10,0) (10,1) (10,2) (10,3) (10,4) (10,5) (10,6) (10,7) (10,8) (10,9) (10,10) 
    
    def from0to10() = shift { (cont: Int => Unit) =>
       for ( i <- 0 to 10 ) {
         cont(i) // call the continuation
       }
    }
    
    def back(action: => Unit) = shift { (cont: Unit => Unit) =>
      cont()
      action
    }
    
    reset {
      val i = from0to10()
      back { println() }
      val j = from0to10
      print(f"${i*j}%4d ") // printf-like formatted i*j
    }
    
       0    0    0    0    0    0    0    0    0    0    0 
       0    1    2    3    4    5    6    7    8    9   10 
       0    2    4    6    8   10   12   14   16   18   20 
       0    3    6    9   12   15   18   21   24   27   30 
       0    4    8   12   16   20   24   28   32   36   40 
       0    5   10   15   20   25   30   35   40   45   50 
       0    6   12   18   24   30   36   42   48   54   60 
       0    7   14   21   28   35   42   49   56   63   70 
       0    8   16   24   32   40   48   56   64   72   80 
       0    9   18   27   36   45   54   63   72   81   90 
       0   10   20   30   40   50   60   70   80   90  100 
    
    def onEven(x:Int) = shift { (cont: Unit => Unit) =>
      if ((x&1)==0) {
        cont() // call continuation only for even numbers
      }
    }
    reset {
      back { println() }
      val x = from0to10()
      onEven(x)
      print(s"$x ")
    }
    
    0 2 4 6 8 10 
    
    // fail: just discard the continuation, force control to return back
    def fail() = shift { (cont: Unit => Unit) => }
    // succ: does nothing (well, passes control to the continuation), but has a funny signature
    def succ():Unit @cpsParam[Unit,Unit] = { }
    // def succ() = shift { (cont: Unit => Unit) => cont() }
    
    reset {
      back { println() }
      val x = from0to10()
      if ((x&1)==0) {
        succ()
      } else {
        fail()
      }
      print(s"$x ")
    }
    
    0 2 4 6 8 10
    
    def onTrue(b:Boolean) = {
      if(!b) {
        fail()
      }
    }
    reset {
      back { println() }
      val x = from0to10()
      onTrue ((x&1)==0)
      print(s"$x ")
    }
    
    0 2 4 6 8 10
    
    // negation: the hard way
    class ControlTransferException extends Exception {}
    def onOdd(x:Int) = shift { (cont: Unit => Unit) =>
      try {
        reset {
          onEven(x)
          throw new ControlTransferException() // return is not allowed here
        }
        cont()
      } catch {
        case e: ControlTransferException =>
        case t: Throwable => throw t
      }
    }
    reset {
      back { println() }
      val x = from0to10()
      onOdd(x)
      print(s"$x ")
    }
    
    1 3 5 7 9