Ответ 1
Сначала давайте посмотрим, что потребуется, чтобы получить 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 => [email protected][Unit, Unit]): [email protected][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
выше, чтобы разрешить общие для понимания.