首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Scala延续的形式化定义

Scala延续的形式化定义
EN

Stack Overflow用户
提问于 2012-01-13 18:36:44
回答 3查看 428关注 0票数 7

有一些关于什么是Scala延续(herehere)的问题。但答案只是试图解释它。因此,在这个问题中,我要求正式定义什么是(Scala的)定界延续。我不需要示例(尽管它可能会有帮助),并要求尽可能简单和易于理解的形式化,如果有帮助,甚至可以忽略键入。

形式化应该涵盖语法(不是在语法意义上,而是类似于f is a function and c is a foo)和语义(计算的结果是什么)。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-15 15:01:21

在延续插件中实现的Scala 分隔的延续是对Danvy和Filinski引入的移位和重置控制操作符的改编。参见他们的抽象控制和表示控制: 1990和1992年CPS转换papers的研究。在类型化语言的背景下,EPFL团队的工作扩展了Asai的工作。请参阅2007年的论文here

这应该是大量的形式主义!我扫了一眼,不幸的是,它们需要流利地使用lambda微积分符号。

另一方面,我发现了以下使用Shift和Reset tutorial的编程,当我开始将示例翻译到Scala时,以及当我读到"2.6如何提取带分隔符的延续“一节时,我感觉自己在理解上真的有了突破。

reset操作符分隔程序的一部分。shift用于存在值的位置(可能包括单位)。你可以把它想象成一个洞。让我们用◉来表示它。

因此,让我们来看看下面的表达式:

代码语言:javascript
运行
复制
reset { 3 + ◉ - 1 }                  // (1)
reset {                              // (2)
  val s = if (◉) "hello" else "hi"
  s + " world"
}
reset {                              // (3)
  val s = "x" + (◉: Int).toString
  s.length
}

shift所做的是将reset中的程序转换为您可以访问的函数(此过程称为具体化)。在上述情况下,函数为:

代码语言:javascript
运行
复制
val f1 = (i: Int) => 3 + i - 1       // (1)
val f2 = (b: Boolean) => {
   val s = if (b) "hello" else "hi"  // (2)
   s + " world"
}
val f3 = (i: Int) => {               // (3)
   val s = "x" + i.toString
   s.length
}

该函数称为延续,并作为参数提供给它自己的参数。移位签名为:

代码语言:javascript
运行
复制
shift[A, B, C](fun: ((A) => B) => C): A 

延续将是(A => B)函数,无论谁在shift中编写代码,都会决定对它做什么(或不做什么)。如果你简单地返回它,你真的能感觉到它能做什么。然后,reset的结果就是具体化的计算本身:

代码语言:javascript
运行
复制
val f1 = reset { 3 + shift{ (k:Int=>Int) => k } - 1 }
val f2 = reset { 
           val s =
             if (shift{(k:Boolean=>String) => k}) "hello"
             else "hi"
           s + " world"
         }
val f3 = reset {
           val s = "x" + (shift{ (k:Int=>Int) => k}).toString
           s.length
         }

我认为物化方面确实是理解Scala分隔延续的一个重要方面。

从类型的角度来看,如果函数k的类型为(A=>B),则shift的类型为A@cpsParam[B,C]。类型C完全由您在shift中选择返回的内容决定。返回带有cpsParamcps注释的类型的表达式在EPFL论文中被限定为impure。这与没有cps注释类型的纯表达式相反。

不纯计算被转换为Shift[A, B, C]对象(现在在标准库中称为ControlContext[A, B, C] )。Shift对象正在扩展延续单元格。它们的正式实现在EPFL第4页3.1节中。map方法将纯计算与Shift对象相结合。flatMap方法将不纯计算与Shift对象结合在一起。

延续插件按照EPLF paper的3.4节中给出的大纲执行代码转换。基本上,shift被转换为Shift对象。之后出现的纯表达式与映射组合,不纯表达式与flatMaps组合(参见图4的更多规则)。最后,一旦所有内容都转换为封闭的重置,如果所有内容都进行了类型检查,那么在所有的map和flatMaps之后,最终的Shift对象的类型应该是Shift[A, A, C]reset函数实现了所包含的标识,并使用Shift函数作为参数调用该函数。

总之,我认为EPLF论文确实包含了对发生的事情的正式描述( 3.1和3.4节以及图4)。我提到的教程有非常好的例子,这些例子给人一种很好的分界延续的感觉。

票数 4
EN

Stack Overflow用户

发布于 2012-01-13 23:21:19

引用wikipedia

定界连续,可组合连续或部分连续,是已被具体化为函数的连续帧的“切片”。

这方面的Scala语法是:

代码语言:javascript
运行
复制
// Assuming g: X => anything
reset {
  A
  g(shift { (f: (X) => Y) => /* code using function f */ })
  B
}

上面的连续帧是在shift之后执行的所有内容,直到由reset分隔的块的末尾。这包括调用函数g,因为它只有在计算完shift以及B中的所有代码之后才会被调用。

函数g不是必需的--您可以改为调用一个方法,或者完全忽略shift的结果。我展示它只是为了说明shift调用返回了一个可以使用的值。

换句话说,继续帧变成以下函数:

代码语言:javascript
运行
复制
// Assuming g: X => anything
def f: (X) => Y = { x => 
    g(x)
    B
}

整个重置主体变成这样:

代码语言:javascript
运行
复制
// Assuming g: X => anything
A
def f: (X) => Y = { x => 
    g(x)
    B
}
/* code using function f */

请注意,B中的最后一条语句必须具有Y类型。计算的结果是shift块的内容的结果,就像上面的转换一样。

如果你想要更高的精确度,可以使用check the paper来描述Scala中的定界延续。确切的类型可以在API documentation上找到。

票数 3
EN

Stack Overflow用户

发布于 2018-09-23 22:29:08

延续允许你在移位后(但在重置中)捕获代码,并像这样应用它:

代码语言:javascript
运行
复制
  import scala.util.continuations._
  def main(args: Array[String]): Unit = {
    reset {
      shift { continue: (Int => Int) =>
        val result: Int = continue(continue(continue(7)))
        println("result: " + result) // result: 10
      } + 1
    }
  }

在这种情况下,shift外部(但在reset内部)的代码是+1,所以每次调用continue时,都会应用{ _+1 }。因此,continue(continue(continue(7)))的结果是7+1+1+ 1,即10。

下面是取自here的更多示例代码

代码语言:javascript
运行
复制
  import scala.util.continuations._
  import java.util.{Timer,TimerTask}

  def main(args: Array[String]): Unit = {
    val timer = new Timer()
    type ContinuationInputType = Unit
    def sleep(delay: Int) = shift { continue: (ContinuationInputType => Unit) =>
      timer.schedule(new TimerTask {
        val nothing: ContinuationInputType = ()
        def run() = continue(nothing) // in a real program, we'd execute our continuation on a thread pool
      }, delay)
    }
    reset {
      println("look, Ma ...")
      sleep(1000)
      println(" no threads!")
    }
  }

在上面的代码中,在移位之后但在重置内部的代码是println(" no threads!")。因此,如果我们替换这个:

def run() = continue(nothing)

有了这个:

def run() = continue(continue(continue(nothing)))

我们得到以下输出:

代码语言:javascript
运行
复制
look, Ma ...
 no threads!
 no threads!
 no threads!

而不是下面的输出:

代码语言:javascript
运行
复制
look, Ma ...
 no threads!

因此,我们在此更改后的代码基本上等同于:

代码语言:javascript
运行
复制
  import java.util.{Timer,TimerTask}
  def main(args: Array[String]): Unit = {
    println("look, Ma ...")
    timer.schedule(new TimerTask {
      def run() = {
        println(" no threads!")
        println(" no threads!")
        println(" no threads!")
      }
    }, 1000)
  }

您可以尝试使用代码here

请注意,在调用continue之前的所有代码都只执行一次,而shift结束和reset结束之间的所有代码执行的次数与调用continue的次数相同。如果我们的continuation从未被调用过,那么位于shift结束和reset结束之间的代码就永远不会被执行。因此,Scala中的延续是一个lambda,它捕获一个移位结束和该移位的封闭重置结束之间的所有代码。

还要注意,如果在线程池上执行延续,则其余代码( shift结束和重置结束之间的所有代码)将在该线程池提供给我们的线程上执行。因此,如果我们的continuation在线程池线程#1上运行,那么println(" no threads!")将在线程池线程#1上运行,但是println("look, Ma ...")将在主线程上运行。正因为如此,延续特性可用于在异步I/O之上实现外观,使其看起来像阻塞IO。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8849185

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档