Сначала давайте посмотрим, что потребуется для получения gen2
для компиляции.
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
не может быть скомпилирована в следующей строке:
ints.foreach((theInt) => yieldValue(theInt))
Поскольку тип yieldValue
включает аннотацию @cpsParam
, плагин продолжений преобразует функцию, переданную методу foreach
, водин из типов:
Int => Unit @cpsParam[Unit,Unit]
Поднявшись по иерархии List[Int]
, вы увидите foreach
, определенный как:
foreach [U] (f: (Int) ⇒ U): Unit
Это проблема, так как типыне совпадают, и Scala не знает, как добраться от Int => U
до Int => Unit @cpsParam[Unit,Unit]
.Чтобы исправить это, я добавил CPS-версию foreach
в неявном преобразовании, доступ к которому можно получить, вызвав cps
для любого IterableLike
.
. Было бы очень хорошо, если бы это неявное преобразование могло бытьсделано без явного вызова cps
, но я не нашел способа заставить компилятор Scala распознать применимость такого неявного преобразования для добавления нового foreach
в ваш список.Это может быть связано с порядком, в котором компилятор использует плагин продолжения, но я слишком мало знаю об этом процессе, чтобы быть уверенным.
Так что это все хорошо для foreach
.Ваш вопрос упоминается для понимания, для которого потребуется определить любой из filter
, map
или flatMap
(в зависимости от того, что происходит в вашем понимании).Я реализовал их в ссылке в моем комментарии выше, которая расширяет объект CpsConversions
, указанный выше, для общего понимания.