什么是卡拉的延续,为什么使用它们?
-
19-09-2019 - |
题
我只是完成了 编程在卡拉, 和我已经找到的变化之间的卡拉2.7和2.8.一个似乎是最重要的是继续插件,但我不明白为什么有用的,或者它是如何工作的。我已经看到,它的良好步I/O,但是我还没找出为什么。更受欢迎一些资源的主题是:
和这个问题上堆溢出:
不幸的是,这些文献没有试图界定什么样的延续是为什么或什么的移/复功能所应该做的,我还没有找到任何引用。我一直没能猜到如何的任何例子链接的文章的工作(或者什么他们这样做),这样一种方式来帮助我可以去逐行通过一些样本。即使这种简单的从一个第三段:
reset {
...
shift { k: (Int=>Int) => // The continuation k will be the '_ + 1' below.
k(7)
} + 1
}
// Result: 8
为什么是结果是8?这可能会帮助我获得启动。
解决方案
我 博客 不解释什么 reset
和 shift
这样做,所以你可以想再读一遍。
另一个良好来源,这也是我点在我的博客,是维基百科上的条目 继续通过风格.那一个是迄今为止,最明显的问题,虽然它不使用卡拉语法,并将继续是明确通过。
纸上分隔的延续,这是我的链接在我的博客,但似乎已经成为破坏,使许多实例的使用情况。
但我认为最好的例子 概念 分隔的延续是斯群。在它,图书馆 停止 执行你的代码一点,其余的计算成本的延续。图书馆然后做一些事情--在这种情况下,转移到另一个主机计算,返回的结果(价值的变量其进行访问)的计算,是停止。
现在,你不了解即使是简单的例子在卡拉页面,所以 做 读过我的博客。在这我 只 关于说明这些基础知识,为什么结果 8
.
其他提示
我发现现有的解释可少的有效解释的概念比我会的希望。我希望这个是明确的(和正确的。) 我已经不用的延续。
当一个延续的功能 cf
被称为:
- 执行跳过其他的
shift
块,并开始再一次在它的结束- 的参数过来
cf
是什么shift
方框"评估",以作为继续执行。这可以是不同的,对每一个呼叫cf
- 的参数过来
- 继续执行,直到结束
reset
方块(或直到呼叫reset
如果没有方块)- 结果的
reset
方块(或参数reset
()如果没有框)是什么cf
返回
- 结果的
- 执行后继续
cf
直到结束shift
块 - 执行跳过,直到结束
reset
方块(或呼叫的重置?)
因此,在这个例子中,按照字母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
鉴于典型例子,从 研究论文 为卡拉的分隔的延续,稍加修改,使功能输入到 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
斯卡拉插件转换这一例这样的计算(在输入参数的 reset
)从每 shift
援引的 reset
是 替换 用功能(例如 f
)输入来 shift
.
更换的计算 移 (即移动)的成功 k
.功能 f
输入功能 k
, ,哪里 k
包含 更换的计算, k
输入 x: Int
, 和计算 k
替换 shift(f)
与 x
.
f(k) * 2
def k(x: Int): Int = x + 1
它具有同样的效果:
k(k(k(7))) * 2
def k(x: Int): Int = x + 1
注意的类型 Int
输入参数 x
(即类型签名的 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)
}
}
我希望这种阐明连贯一致共同的抽象这是有些模糊通过现有的介绍的这两个例子。例如,规范的第一个例子是呈现在 研究论文 作为一个匿名的功能,而不是我命名的 f
, ,因此,它不清楚到一些读者,这是抽象的类似的 read
在 借来的 第二个例子。
因此分隔的延续创造的幻觉的一种反转的控制从"你叫我来自外面的 reset
"到"我叫你在里面 reset
".
注意返回的类型 f
是的,但 k
不是,必须同返回的类型 reset
, 即 f
有自由的声明的任何返回的类型 k
只要 f
返回相同的类型 reset
.同上 read
和 capture
(也请参看 ENV
下文)。
分隔的延续不隐含的倒置的控制状态,例如 read
和 callback
不是纯粹的功能。这样的呼叫者不能引用创建透明的表达,因此没有 声明(a。k.a.透明)控制通过旨在必须义.
我们可以明确地实现纯粹的职能分隔的延续。
def aread(env: ENV): Tuple2[Byte,ENV] {
def read(callback: Tuple2[Byte,ENV] => ENV): ENV = env.myCallback(callback)
shift(read)
}
def pure(val env: ENV): ENV {
reset {
val (byte1, env) = aread(env)
val env = env.println("byte1 = " + byte1)
val (byte2, env) = aread(env)
val env = env.println("byte2 = " + byte2)
}
}
我相信这将是翻译的逻辑相当于:
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)
}
}
}
这是越来越吵,因为明确的环境。
切地注意到,卡拉没有Haskell的全球类型推理,因此尽我知道不能隐含的支持提升到一个国家单的 unit
(作为一个可能的战略为隐藏的明确的环境),因为Haskell的全球(辛德雷-米尔纳)的类型推断取决于 不支持钻石多个虚拟的继承.
继续捕获的状态计算的,是援引以后。
认为计算之间离开移表达和离开的重置表达作为一个功能。内部转移表达这种功能是所谓的k,这是在延续。你可以通过它的周围,援引了它之后,甚至超过一次。
我认为值返回复表达的价值的表达的内部转移表达之后,=>,但关于这个我不太肯定的。
因此,与的延续你可以用一个相当任意的和非本地块代码在一个功能。这可以用来执行非标准控制流程,例如coroutining或者回溯。
所以延续应用系统上的水平。洒他们通过应用程序码将是一个确定配方的噩梦,更糟比这糟糕的意大利面条码使用转到可能永远不会。
免责声明: 我没有深入的理解继续在卡拉,我只是推断出它从看的例子,知道继续从方案。
从我的角度来看,最好的解释给了这里: http://jim-mcbeath.blogspot.ru/2010/08/delimited-continuations.html
一个例子:
看到控制的流动多一点很清楚,可以执行这 code snippet:
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
另一个(多个最近--可能于2016年)第服务的延续:
"时间旅行在拉斯卡拉:CPS在卡拉(斯卡拉的延续)"通过
Shivansh Srivastava(shiv4nsh
).
它还指 吉姆McBeath's 文章 中提到的 德米特里*别斯帕洛夫's 答案.
但在这之前,它描述了继续像这样:
继续是一个抽象表示的控制状态的一个计算机程序.
那么,这实际上意味着是,它是一个数据结构表示的计算处理在给定点的过程的执行;所创建的数据结构可访问的编程语言,而不是被隐藏在运行环境。解释一步,我们可以有一个最典型的例子,
说你是在厨房里面前的冰箱,思考一个三明治。你把一个继续有权利,并把它放在你的口袋里。
然后你会得到一些土耳其和面包出来的冰箱和自己做三明治,这现在是坐在柜台上。
你调用的延续在你的口袋里,你发现自己站在冰箱里再次,考虑一个三明治。但幸运的是,有一个三明治上的反,所有使用的材料,以使它们走了。所以你吃了它。:-)在此说明,
sandwich
是的一部分 程序的数据 (例如,对象堆上),而不是调用了一个"make sandwich
"程序,然后返回时,人称"make sandwich with current continuation
"例行程序,它创建的三明治,然后继续在那里执行。
这就是说,作为宣布中 四月2014年卡拉2.11.0-RC1
我们寻求维护人员采取过以下模块: 卡拉-摇摆, 卡拉-继续.
2.12将不包括他们如果没有新的维护者被发现.
我们很可能会继续保持其他的模块(斯卡拉-xml,卡拉-parser-组合子),而是帮助仍然是极大的赞赏。