Продолжения и для понимания - что такое несовместимость? - PullRequest
10 голосов
/ 20 января 2012

Я новичок в Scala и пытаюсь обернуть голову вокруг продолжений. Я пытаюсь воспроизвести оператор yield return C #.

После этого поста я написал следующеекод:

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 будет работать так же хорошо.

Ошибка компиляции следующая:

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?

Спасибо

1 Ответ

4 голосов
/ 23 января 2012

Сначала давайте посмотрим, что потребуется для получения 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, указанный выше, для общего понимания.

...