2012-01-19 7 views
10

Soy nuevo en Scala y trato de ajustarme a las continuas Estoy intentando reproducir la declaración yield return C#.Continuaciones y para comprensión - ¿Cuál es la incompatibilidad?

Siguiendo this post, he escrito el siguiente código:

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

Como se puede ver, gen2 esté comentada, ya que no se compila. Como puedo iterar fácilmente sobre el contenido de una lista usando un ciclo while (ver gen3), esperaba que el bucle foreach funcionara igual de bien.

El error de compilación es la siguiente:

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 

¿Por qué aparece este error y hay una manera de evitar esto con algo más limpio que un bucle while?

Gracias

+2

posible duplicado de [Implementar el rendimiento (retorno de rendimiento) utilizando las continuaciones de Scala] (http: // stackoverflow.com/questions/2201882/implementation-yield-yield-return-using-scala-continuations) –

+0

No estoy buscando una respuesta que me diga cómo reproducir el "retorno de rendimiento" con las continuas de Scala. Estoy buscando la razón por la cual el 'gen2' en mi ejemplo no funciona. El 'retorno de rendimiento' es solo el contexto en el que encontré este problema. – GuiSim

+0

Ah, lo siento entonces. –

Respuesta

4

En primer lugar vamos a ver lo que se necesita para obtener gen2 para compilar.

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

Ahora echemos un vistazo a lo que está sucediendo. El original gen2 falla al compilar en la línea siguiente:

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

Dado que el tipo de yieldValue incluye una anotación @cpsParam, las continuaciones Plugin transforma la función pasado al método foreach a uno de tipo:

Int => Unit @cpsParam[Unit,Unit] 

manera para arriba en la jerarquía de List[Int], verá foreach definido como:

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

Esto es un problema, ya que los tipos no coinciden y Scala no sabe cómo obtener desde Int => U hasta Int => Unit @cpsParam[Unit,Unit]. Para solucionarlo, agregué la versión CPS de foreach en una conversión implícita, a la que puede acceder llamando al cps en cualquier IterableLike.

Sería muy bueno si esta conversión implícita podría hacerse sin la explícita cps llamada, pero no he encontrado una manera de hacer que el compilador Scala reconocer la aplicabilidad de una tal conversión implícita para chulo el nuevo foreach en su lista . Esto podría tener que ver con el orden en que el compilador utiliza el complemento de continuación, pero sé muy poco acerca de este proceso para estar seguro.

Así que eso está muy bien para foreach. Su pregunta menciona la comprensión, que requerirá cualquiera de filter, map o flatMap por definir (dependiendo de lo que ocurra en su comprensión). Los he implementado en el enlace en mi comentario anterior, que extiende el objeto CpsConversions de arriba para permitir general para las comprensiones.

Cuestiones relacionadas