我一直试图用scala continuation来解决复杂的输入问题。我一直在阅读我能找到的所有材料,包括延续包中的参考文档。我认为我已经在某种程度上弄明白了,当你想到它时它会变得很有意义。
我认为我对它的理解(以及我的一些问题)可以通过这个程序得到最好的总结:
package com.whatever;
import scala.util.continuations._;
object methods {
/* The method takes an Int as its parameter. Theoretically, at some point in the future,
* it will return a Float to the remainder of the continuation. This example does it
* immediately but doesn't have to (for example it could be calling a network service
* to do the transformation)
*
* Float @cpsParam[Unit,Float] means that whatever part of the reset{} that is captured
* as a closure should receive a Float and needn't return anything (would it be meaningful
* if Unit were something else?)
*
* The reason I have to return 0.toFloat is so the compiler can properly type the
* method. That zero will never go anywhere. Is this a sign I'm doing it wrong?
*/
def method1(param:Int): Float @cpsParam[Unit,Float] = shift { cb:(Float=>Unit) =>
cb(param.toFloat);
0.toFloat;
}
/* This method is basically identical but returns a String instead of a Float (Again,
* theoretically this would be done by a network service and cb would be called at some
* point in the future.
*/
def method2(param:Int): String @cpsParam[Unit,String] = shift { cb:(String=>Unit) =>
cb(param.toString);
""
}
}
object Main {
def main(args:Array[String]):Unit = {
reset {
val f = methods.method1(5);
println(f);
}
}
}
顺便说一句,StackOverflow没有突出显示scala是犯罪行为!(我站得更正;它确实做得很好,但不是在实时预览中)
我的问题如下:
Unit
中B
@cpsParam[B,C]
作为"5.0"
?reset
)。但是,我现在遇到的问题是,当我更改method2
块以尝试在method1
之后调用reset {
val f = methods.method1(5);
println(f);
val s = methods.method2(42);
println(s);
}
时,我遇到了这个问题:(显然你不能在列表后面有代码块)
illegal answer type modification: scala.util.continuations.cpsParam[Unit,Float] andThen scala.util.continuations.cpsParam[Unit,String]
当我这样做时(这看起来很简单),我在重置时得到以下编译器错误(这是scala 2.10里程碑2):
{{1}}
我将此解释为“你的第一个班次返回一个浮点数,你的第二个班次返回一个字符串,你不能这样做。”这准确吗?这是否意味着你不能使用CPS按顺序执行两个(或更多)事情,除非它们具有相同的返回类型?因为这似乎是一种严重的限制。我猜测我要么是1)缺少允许你这样做的东西,或者B)遗漏了一些明显的原因,为什么CPS不可能发生这种情况。但是它是哪一个?
为了理解scala的CPS,我开始觉得你不需要成为博士后学生。但我当然还没有。
答案 0 :(得分:4)
在我提出这个问题之后,我做了更多的研究,我想我现在能够回答我自己的问题(我希望这不是一个失礼)。
我做了三件事让我理解了这个问题,而且我认为任何在scala延续中遇到问题的人都会采取以下措施:
shift
的类型签名,并注意它正在做什么。这将带你进入我的顿悟。在我的情况下,我输入了@cpsParam
和cb
参数shift
都错了。我将解释我是如何弄清楚我做错了什么的,这样任何像我一样愚蠢的人都可以遵循相同的步骤,并希望在延续编译器驱动它们时获得一些见解 mad
我阅读了上述文章。大约十几次。我仍然很清楚。但我所理解的是非常有帮助的。
我在回调传递样式中重写了reset
块,假装不是shift
,而是每个方法都有一个名为cb
的第二个参数,它需要一个函数来做块的其余部分。这是重置块在此之后的样子:
methods.method1(5, {f: Int => {
println(f);
methods.method2(42, {s: String => {
println(s);
});
});
看看发生了什么?所以现在我没有编写看似阻塞的代码,而是自己明确地分隔连续性并将它们作为函数传递给每个方法。因此,对于这些情况中的每一种情况,很明显我的每个匿名回调函数都不需要返回任何内容,事实上,它们都应该返回Unit
,否则它们将污染它们的方法的返回类型正在被传入。我认为这就是编译器试图告诉我的内容(尽管我可能错了)。
这里的method1
对于我的回调式程序来说应该是什么样子
def method1(param:Int, cb:(Float=>Unit)):Unit = {
cb(param.toFloat);
}
method2
类似,但需要(String=>Unit)
。现在很明显我的方法也应该返回Unit
,否则它们可能会污染回调函数的返回类型。
我认为我的很多困惑源于这样一个事实:由于某种原因,我头脑中的图片是每个shift
只捕获到下一个shift
作为延续。当然,情况并非如此;每个shift
必须捕获reset
块的整个剩余,包括以下所有shift
s,以便它形成一个大的嵌套回调函数情况。此外,所有回调和所有CPS调用的方法应该总是(据我所知)返回Unit
,因为它们的结果不仅不会做任何事情,而且会污染函数的返回类型调用它们,依此类推回调链。
现在我查看了shift
的签名。它就在我面前:
def shift[A,B,C](fun: (A => B) => C)): A @cpsParam[B,C]
当我看到这个时,我意识到结合我的回调式练习,这里有足够的信息(即使没有完全理解幕后的shift
做什么)把它变成基本上是一个练习在维度分析中。
我知道method1
的结果将是Float
。因此,继续回调(在上面表示为(A => B)
)需要接受Float
作为其参数。这会将A
修复为Float
。因此,method1
现在看起来像这样:
def method1(param:Int): Float @cpsParam[B,C] = shift { cb: (Float => B) => {
...
C
}
}
换句话说,我传递给shift
的函数必须从Float到B取一个函数,然后返回C.我从练习中知道回调应该返回Unit或者事情变得混乱。我也知道在我的回调练习中,方法本身应该返回Unit
,因为它们将实际结果作为参数传递给延续。这类似于C也是单位。所以这意味着method1
必须如下所示:
def method1(param:Int): Float @cpsParam[Unit,Unit] = shift { cb: (Float => Unit) => {
cb(param);
}
}
method2
将是相同的,除了回调将采用字符串。
现在在我看来,你可以简单地记住,如果你正在编写一个回调驱动的程序,几乎所有涉及的函数都会返回Unit
,而不是被所有类型参数混淆。因为任何结果都作为参数传递而不是被返回。
这意味着,据我所知,B
中的C
和shift
成为其他任何目标的目的不大比Unit
。这是完全合理的,因为有一个注释@suspendable
,它是@cps[Unit]
的快捷方式,是@cpsParam[Unit,Unit]
的快捷方式。
我不知道为什么scala-lang.org上的例子就是这样的废话。但实际上他们需要说的是,"如果你需要使用除MyReturnType @suspendable
之外的任何东西,那么你可能做错了,顺便说一句shift
所采用的函数参数也应该可能会返回Unit
。"然后,我仍然会度过我生命中最后几个宝贵的日子。
上面提到的具有更改的程序完全编译并按顺序运行这两种方法。所以这让我相信我终于做对了。如果您是具有深入了解CPS的博士,那么请纠正我的遗嘱中的任何不准确之处。