2012-01-09 7 views
18

Estoy tratando de entender lo que Scala hace con Case Classes que los hace de alguna manera inmunes a las advertencias de borrado de tipo.Scala: clase de caso unapply vs una implementación manual y tipo de borrado

Digamos que tenemos la siguiente estructura de clase simple. Se trata básicamente de un Either:

abstract class BlackOrWhite[A, B] 

case class Black[A,B](val left: A) extends BlackOrWhite[A,B] 

case class White[A,B](val right: B) extends BlackOrWhite[A,B] 

y que está tratando de utilizar de esta manera:

object Main extends App { 

    def echo[A,B] (input: BlackOrWhite[A,B]) = input match { 
     case Black(left) => println("Black: " + left) 
     case White(right) => println("White: " + right) 
    } 

    echo(Black[String, Int]("String!")) 
    echo(White[String, Int](1234)) 
} 

Todo compila y se ejecuta sin ningún problema. Sin embargo, cuando intento implementar el método unapply, el compilador lanza una advertencia. He utilizado la siguiente estructura de clases con la misma clase Main arriba:

abstract class BlackOrWhite[A, B] 

case class Black[A,B](val left: A) extends BlackOrWhite[A,B] 

object White { 

    def apply[A,B](right: B): White[A,B] = new White[A,B](right) 

    def unapply[B](value: White[_,B]): Option[B] = Some(value.right) 

} 

class White[A,B](val right: B) extends BlackOrWhite[A,B] 

Compilación de que con los temas bandera -unchecked la siguiente advertencia:

[info] Compiling 1 Scala source to target/scala-2.9.1.final/classes... 
[warn] src/main/scala/Test.scala:41: non variable type-argument B in type pattern main.scala.White[_, B] is unchecked since it is eliminated by erasure 
[warn]   case White(right) => println("White: " + right) 
[warn]     ^
[warn] one warning found 
[info] Running main.scala.Main 

Ahora, entiendo el tipo de borrado y yo he tratado de evitar la advertencia con Manifests (en vano hasta ahora), pero ¿cuál es la diferencia entre las dos implementaciones? ¿Las clases de casos están haciendo algo que necesito agregar? ¿Se puede eludir esto con Manifests?

Incluso he intentado ejecutar la implementación de la clase caso a través del compilador de Scala con la bandera -Xprint:typer activada, pero el método unapply ve muy parecido a lo que esperaba:

case <synthetic> def unapply[A >: Nothing <: Any, B >: Nothing <: Any](x$0: $iw.$iw.White[A,B]): Option[B] = if (x$0.==(null)) 
    scala.this.None 
else 
    scala.Some.apply[B](x$0.right); 

Gracias de antemano

+0

¿Estás Usando la última versión de Scala, no puedo reproducir su problema, y ​​esta pregunta relacionada de hace unos meses determinó un problema similar al suyo como un error del compilador. Consulte http://stackoverflow.com/questions/7008428/difference -entre-home-made-extractor-and-case-class-extractor – Destin

+0

Estoy usando 2.9.1.final (En Xubuntu 11.10, si es importante) – Nycto

Respuesta

12

no puedo dar una respuesta completa, pero puedo decirle que aunque el compilador genera un método unapply para las clases de casos, cuando el patrón coincide con una clase de caso, no utiliza ese método de aplicación. Si prueba -Ybrowse:typer usando la coincidencia de casos incorporada y su método unapply, verá que se produce un árbol de sintaxis muy diferente (para el match) según cuál se utilice. También puede navegar por las fases posteriores y ver que la diferencia permanece.

Por qué Scala no usa la aplicación integrada no estoy seguro, aunque puede ser por la razón por la que aparece. Y cómo solucionarlo por tu cuenta unapply No tengo ni idea. Pero esta es la razón por la que Scala parece esquivar el problema mágicamente.

Después de experimentar, al parecer, esta versión de unapply obras, aunque estoy un poco confundido acerca de por qué:

def unapply[A,B](value: BlackOrWhite[A,B]): Option[B] = value match { 
    case w: White[_,_] => Some(w.right) 
    case _ => None 
} 

La dificultad con su unapply es que de algún modo el compilador tiene que estar convencido de que si un White[A,B] se extiende un BlackOrWhite[C,D] luego B es lo mismo que D, que aparentemente el compilador puede descifrar en esta versión pero no en la suya. No estoy seguro por qué.

+2

En realidad, 'unapply' se creó para que el usuario pueda replicar la funcionalidad esa 'clase de caso' pr Ovided. Hay algunos otros lugares donde la explicación oficial de cómo funcionan las cosas no es cómo lo hace realmente el compilador. –

5

No puedo darle la respuesta sobre la diferencia entre la coincidencia de clase de caso y la aplicación. Sin embargo, en su libro (Odersky, Spoon, Venners) "Programming in Scala" 2nd chptr 26.6 "Extractores frente a las clases de casos", escriben:

"ellos (clases de casos) por lo general conducen a patrón más eficiente partidos de extractores, porque el compilador Scala puede optimizar patrones más clases de casos mucho mejor que los patrones de más de extractores Esto es porque los mecanismos de las clases de casos son fijos, mientras que aplicar un método o unapplySeq en un extractor podría hacer casi cualquier cosa. Tercero, si sus clases de caso heredan de una clase base sellada, el compilador de Scala comprobará nuestro patrón coincide con la exhaustividad y se se queja si una combinación de valores posibles no está cubierta por un patrón. No hay tales controles de exhaustividad están disponibles para extractores."

¿Qué me dice que los dos son más diferentes de lo que cabría esperar a primera vista, sin embargo, sin especificar de cuáles son las diferencias exactas.

Cuestiones relacionadas