延续和理解-不兼容是什么? [英] Continuations and for comprehensions -- what's the incompatibility?
问题描述
我是Scala的新手,并试图将自己的头缠在延续上
我正在尝试重现yield return
C#语句.
I am new to Scala and trying to wrap my head around continuations
I'm trying to reproduce the yield return
C# statement.
这篇文章之后,我编写了以下代码:
Following this post, I have written the following code :
package com.company.scalatest
import scala.util.continuations._;
object GenTest {
val gen = new Generator[Int] {
def produce = {
yieldValue(1)
yieldValue(2)
yieldValue(3)
yieldValue(42)
}
}
// Does not compile :(
// val gen2 = new Generator[Int] {
// def produce = {
// var ints = List(1, 2, 3, 42);
//
// ints.foreach((theInt) => yieldValue(theInt));
// }
// }
// But this works?
val gen3 = new Generator[Int] {
def produce = {
var ints = List(1, 2, 3, 42);
var i = 0;
while (i < ints.length) {
yieldValue(ints(i));
i = i + 1;
}
}
}
def main(args: Array[String]): Unit = {
gen.foreach(println);
// gen2.foreach(println);
gen3.foreach(println);
}
}
abstract class Generator[E] {
var loopFn: (E => Unit) = null
def produce(): Unit @cps[Unit]
def foreach(f: => (E => Unit)): Unit = {
loopFn = f
reset[Unit, Unit](produce)
}
def yieldValue(value: E) =
shift { genK: (Unit => Unit) =>
loopFn(value)
genK(())
()
}
}
如您所见,由于gen2
未编译,因此已被注释掉.由于我可以使用while循环轻松访问列表的内容(请参见gen3
),因此我希望foreach循环也能正常工作.
As you can see, gen2
is commented out as it does not compile. Since I can easily iterate over the content of a list using a while loop (see gen3
), I expected the foreach loop to work just as well.
编译错误如下:
no type parameters for method foreach: (f: Int => B)Unit exist so that
it can be applied to arguments (Int => Unit @scala.util.continuations.cpsParam[Unit,Unit])
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Int => Unit @scala.util.continuations.cpsParam[Unit,Unit]
required: Int => ?B
为什么我会收到此错误,并且有一种方法可以用比while循环更干净的方法来解决此问题?
谢谢
推荐答案
首先让我们看一下gen2
进行编译的过程.
First let's look at what it will take to get gen2
to compile.
object CpsConversions {
import scala.collection.IterableLike
import scala.util.continuations._
implicit def cpsIterable[A, Repr](xs: IterableLike[A, Repr]) = new {
def cps = new {
def foreach[B](f: A => Any@cpsParam[Unit, Unit]): Unit@cpsParam[Unit, Unit] = {
val it = xs.iterator
while(it.hasNext) f(it.next)
}
}
}
}
object GenTest {
import CpsConversions.cpsIterable
val gen2 = new Generator[Int] {
def produce = {
var ints = List(1, 2, 3, 42)
ints.cps.foreach((theInt) => yieldValue(theInt))
}
}
现在让我们看看发生了什么.原始的gen2
无法在以下行上编译:
Now let's take a look at what's going on. The original gen2
fails to compile on the following line:
ints.foreach((theInt) => yieldValue(theInt))
由于yieldValue
的类型包括@cpsParam
批注,所以continuation插件将传递给foreach
方法的函数转换为以下类型之一:
Since the type of yieldValue
includes an @cpsParam
annotation, the continuations plugin transforms the function passed to the foreach
method to one of type:
Int => Unit @cpsParam[Unit,Unit]
在List[Int]
的层次结构中,您会看到foreach
定义为:
Way up in the hierarchy of List[Int]
, you'll see foreach
defined as:
foreach [U] (f: (Int) ⇒ U): Unit
这是一个问题,因为类型不匹配并且Scala不知道如何从Int => U
到Int => Unit @cpsParam[Unit,Unit]
.为了解决此问题,我在隐式转换中添加了foreach
的CPS版本,您可以通过在任何IterableLike
上调用cps
进行访问.
This is a problem, as the types do not match and Scala doesn't know how to get from Int => U
to Int => Unit @cpsParam[Unit,Unit]
. To fix it, I added the CPS version of foreach
in an implicit conversion, which you can access by calling cps
on any IterableLike
.
如果不用显式的cps
调用就可以完成这种隐式转换,那将是非常好的,但是我还没有找到一种使Scala编译器认识到这种隐式转换对新的foreach
皮条客的适用性的方法.到您的列表中.这可能与编译器使用continuation插件的顺序有关,但是我对这一过程了解得很少,无法确定.
It would be very nice if this implicit conversion could be done without the explicit cps
call, but I have not found a way to make the Scala compiler recognize the applicability of such an implicit conversion to pimp the new foreach
onto your list. This might have to do with the order in which the compiler uses the continuations plugin, but I know far too little about this process to be sure.
所以对于foreach
来说一切都很好.您的问题提到了理解,这将要求定义filter
,map
或flatMap
中的任何一个(取决于您对理解的理解).我已经在上面的评论的链接中实现了这些内容,该链接扩展了上面的CpsConversions
对象以允许进行一般理解.
So that's all well and good for foreach
. Your question mentions for comprehensions, which will require any of filter
, map
, or flatMap
to be defined (depending on what goes on in your for comprehension). I have implemented these in the link in my above comment, which extends the CpsConversions
object above to allow for general for comprehensions.
这篇关于延续和理解-不兼容是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!