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))
}
}
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))
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]
Way up in the hierarchy of List[Int]
, you'll see foreach
defined as:
foreach [U] (f: (Int) ? U): Unit
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
.
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.
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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…