我刚刚完成编程在Scala中 ,我一直在寻找到斯卡拉2.7和2.8之间变化。 这似乎是最重要的一个是延续插件,但我不明白什么是有用的还是它的工作原理。 我已经看到了它的良好的异步I / O,但我一直没能找出原因。 一些关于这个问题比较流行的资源是这些:
- 分隔延续和Scala
- 后藤在斯卡拉
- 2.8味道:延续
- 分隔的延续解释(Scala中)
和堆栈溢出这样的问题:
- 什么是斯卡拉2.8和Scala 2.7之间最大的区别是什么?
不幸的是,这些文献中没有尝试定义是什么延续了还是什么移位/复位功能都应该做的,我还没有发现,做任何引用。 我一直没能猜出如何任何的链接的文章中的例子工作(或他们做了什么),这样的一种方式来帮助我可以通过这些样品的人去行由行。 即使这样简单的一个,从第三条规定:
reset {
...
shift { k: (Int=>Int) => // The continuation k will be the '_ + 1' below.
k(7)
} + 1
}
// Result: 8
为什么结果8? 这可能会帮助我上手。
我的博客不会解释什么reset
和shift
做,所以你可能需要再次阅读。
另一个很好的来源,这也是我在我的博客点,是在维基百科条目延续传递风格 。 那一个是,到目前为止,关于这个问题的最明确的,虽然它不使用Scala的语法和延续明确传递。
在分隔的延续,这是我链接到我的博客,但似乎已成为破纸,给人使用的例子很多。
但我认为分隔延续概念的最好的例子就是斯卡拉群。 在这里面,库将停止代码的执行在一个点上,余下的计算变得延续。 然后该库做了 - 在这种情况下,计算转移到另一台主机,并返回结果(这是访问的变量的值)已停止计算。
现在,你不理解甚至斯卡拉页面上简单的例子,所以不要看我的博客。 在这里面我只关心解释为什么结果是这些基本知识, 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
鉴于从典型的例子研究论文对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的插件变换本实施例中,使得所述计算(的输入参数中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
不是纯粹的功能。 因此,呼叫者不能创建引用透明表达式,因此不会有过度旨在势在必行语义的声明性(又名透明)控制 。
我们可以明确地实现与分隔延续纯函数。
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)
}
}
}
这是越来越嘈杂,因为明确的环境。
切向注,Scala没有Haskell的全局类型推断,因此,据我所知不能支持隐式提升到一个状态单子的unit
(如用于隐藏明确的环境中一个可能的策略),因为Haskell的全局(辛德米尔纳)型推论依赖于不支持钻石多个虚拟继承 。
延续捕捉计算的状态,稍后调用。
认为离开变换表达式和离开复位表达为函数之间的计算的。 内部移位表达这个函数被称为k,它是延续。 你可以通过它周围,以后调用它,甚至是多次。
我想通过复位表达式返回的值是=移位后的表达中的表达式>的值,但是这个我不太清楚。
因此,与延续,你可以包装起来的代码相当武断的和非本地件的功能。 这可以被用来实现非标准控制流程,如coroutining或回溯。
所以延续应该在系统级使用。 洒他们通过你的应用程序代码将是一个肯定会做噩梦,不是使用goto语句可能永远是最糟糕的面条代码差很多。
免责声明:我没有在斯卡拉延续的深入了解,我只是推测它看例子,从方案认识的延续。
从我的观点来看,最好的解释是在这里给出: http://jim-mcbeath.blogspot.ru/2010/08/delimited-continuations.html
一个例子:
要查看控制流一点更加明确,可以执行该代码片段:
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年5月)在斯卡拉延续的文章是:
“ 时间旅行在斯卡拉:CPS在斯卡拉(Scala的延续) ”由Shivansh塔瓦( shiv4nsh
) 。
它也指吉姆McBeath的文章中提到梅德Bespalov s'的答案 。
但在此之前,它描述像这样延续:
的延续是一个计算机程序的控制状态的抽象表示 。
那么,它实际上的意思是,这是代表在该过程的执行给定的点计算过程的数据结构; 创建的数据结构可以通过编程语言来访问,而不是隐藏在运行时环境。
为了进一步解释,我们可以拥有最经典的例子之一,
说你是在冰箱前面的厨房,想着一个三明治。 您需要延续在那里,并坚持自己的口袋里。
然后你得到一些火鸡和面包从冰箱取出,并让自己的三明治,也就是现在坐在柜台。
您调用延续在你的口袋里,你会发现自己在冰箱站在门前再次思考一个三明治。 但幸运的是,在柜台上的三明治,以及用来制造它都走了所有的材料。 所以,你吃它。 :-)
在本说明书中, sandwich
是程序数据 (例如,在堆上的对象),并且而不是调用“的一部分make sandwich
”例程,然后返回,人称为“ make sandwich with current continuation
”程序,该程序创建三明治,然后继续在那里执行不放过。
话虽这么说,在宣布2014年4月斯卡拉2.11.0-RC1
我们正在寻找维护接管以下模块: 斯卡拉摆动 , 斯卡拉-延续 。
2.12将不包括他们,如果没有新的维护者被发现 。
我们很可能会继续保持其他模块(斯卡拉的XML,斯卡拉-解析器组合子),但帮助仍是极大的赞赏。