2012-01-19 16 views
10

Sono un neofita di Scala e cerco di capovolgere le continuazioni Sto provando a riprodurre l'istruzione C# yield return.Continuazioni e per comprensioni - qual è l'incompatibilità?

seguito this post, ho scritto il seguente codice:

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(()) 
    () 
    } 
} 

Come si può vedere, gen2 sia commentata in quanto non può essere compilato. Poiché posso facilmente eseguire iterazioni sul contenuto di un elenco utilizzando un ciclo while (vedi gen3), mi aspettavo che il ciclo foreach funzionasse altrettanto bene.

L'errore di compilazione è il seguente:

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 

Perché ricevo questo errore e c'è un modo per ovviare a questo con qualcosa di più pulito di un ciclo while?

Grazie

+2

possibile duplicato di [rendimento di implementazione (rendimento restituito) utilizzando le continuazioni di Scala] (http: // stackoverflow.it/questions/2201882/implementation-yield-yield-return-using-scala-continuations) –

+0

Non sto cercando una risposta che mi dica come riprodurre "yield return" usando le continuazioni di Scala. Sto cercando il motivo per cui il 'gen2' nel mio esempio non funziona. Il "rendimento" è solo il contesto in cui ho riscontrato questo problema. – GuiSim

+0

Ah, scusa allora. –

risposta

4

Prima diamo un'occhiata a che cosa ci vorrà per arrivare gen2 per la compilazione.

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)) 
    } 
    } 

Ora diamo un'occhiata a cosa sta succedendo. L'originale gen2 fallisce la compilazione nella seguente riga:

ints.foreach((theInt) => yieldValue(theInt)) 

Poiché il tipo di yieldValue comprende un @cpsParam nota, il plug continuazioni trasforma la funzione passata al metodo foreach ad uno di tipo:

Int => Unit @cpsParam[Unit,Unit] 

Way in alto nella gerarchia di List[Int], vedrete foreach definito come:

foreach [U] (f: (Int) ⇒ U): Unit 

Questo è un problema, poiché i tipi non corrispondono e Scala non sa come ottenere da Int => U a Int => Unit @cpsParam[Unit,Unit]. Per risolvere il problema, ho aggiunto la versione CPS di foreach in una conversione implicita a cui è possibile accedere chiamando cps su qualsiasi IterableLike.

Sarebbe molto bello se questa conversione implicita potrebbe essere fatto senza l'esplicito cps chiamata, ma non ho trovato un modo per rendere il compilatore Scala riconoscere l'applicabilità di una conversione come implicita a pimp il nuovo foreach nel tuo elenco . Questo potrebbe avere a che fare con l'ordine in cui il compilatore usa il plugin di continuazione, ma so per certo su questo processo.

Quindi va tutto bene per foreach. La tua domanda parla di comprensione, che richiederà uno qualsiasi di filter, map o flatMap da definire (a seconda di ciò che accade nella tua comprensione). Li ho implementati nel collegamento nel mio commento sopra, che estende l'oggetto CpsConversions sopra per consentire una generale comprensione.

Problemi correlati