2012-01-09 10 views
18

Sto cercando di capire cosa Scala fa con Case Classes che li rende in qualche modo immuni agli avvisi di cancellazione del tipo.Scala: Case class inapplicata rispetto a un'implementazione manuale e cancellazione di tipo

Diciamo che abbiamo la seguente struttura di classe semplice. E 'fondamentalmente 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] 

E si sta cercando di usare in questo modo:

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

Tutto compila e funziona senza problemi. Tuttavia, quando provo a implementare il metodo unapply da solo, il compilatore genera un avviso. Ho usato la seguente struttura di classe con la stessa Main classe superiore:

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] 

Compilare che con gli -unchecked questioni di bandiera il seguente avviso:

[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 

Ora, capisco la cancellazione di tipo e ho cercato di aggirare l'avviso con Manifests (fino ad ora inutilmente), ma qual è la differenza tra le due implementazioni? Le classi del caso fanno qualcosa che devo aggiungere? Può essere aggirato con Manifests?

Ho anche provato a fare funzionare l'implementazione caso di classe attraverso il compilatore Scala con la bandiera -Xprint:typer acceso, ma il metodo unapply sembra più o meno come mi aspettavo:

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

Grazie in anticipo

+0

Sei utilizzando l'ultima versione di Scala? Non riesco a riprodurre il problema e questa domanda correlata di alcuni mesi fa ha determinato un problema simile al tuo come un bug del compilatore. Consulta http://stackoverflow.com/questions/7008428/difference tra-home-made-extractor-and-case-extractor – Destin

+0

Sto usando 2.9.1.final (Su Xubuntu 11.10, se è importante) – Nycto

risposta

12

non posso dare una risposta completa, ma posso dirti che anche se il compilatore genera un metodo unapply per le classi case, quando esegue il modello delle corrispondenze su una classe case non usa quel metodo unpply. Se si prova con -Ybrowse:typer utilizzando sia la corrispondenza del case integrato che il metodo unapply, verrà visualizzato un albero di sintassi molto diverso (per lo match) a seconda di quale viene utilizzato. Puoi anche navigare tra le fasi successive e vedere che la differenza rimane.

Perché Scala non usa l'unprile incorporato non sono sicuro, anche se potrebbe essere per il motivo che si presenta. E come girarlo per il proprio unapply Non ne ho idea. Ma questa è la ragione per cui Scala sembra evitare magicamente il problema.

Dopo la sperimentazione, a quanto pare questa versione di unapply opere, anche se sono un po 'confuso sul perché:

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

La difficoltà con il tuo unapply è che in qualche modo il compilatore deve essere convinto che se un White[A,B] estende un BlackOrWhite[C,D] quindi B è lo stesso di D, che apparentemente il compilatore è in grado di capire in questa versione ma non nel tuo. Non so perché.

+2

In realtà, 'unapply' è stato creato in modo che l'utente possa replicare la funzionalità che 'case class' pr ovided. Ci sono alcuni altri posti in cui la spiegazione ufficiale di come funzionano le cose non è come il compilatore effettivamente lo fa. –

5

Non riesco a dare la risposta sulla differenza tra la corrispondenza della classe caso e unpply. Comunque nel loro libro (Odersky, Spoon, Venners) "Programming in Scala" 2nd chptr 26.6 "Estrattori contro classi case", che scrivono:

"essi (classi case) di solito portano a modello più efficiente corrisponde di estrattori, perché il compilatore Scala in grado di ottimizzare i modelli oltre classi case molto meglio di modelli oltre estrattori Questo è perché i meccanismi delle classi dei casi sono fissi, mentre un metodo non valido o unpplySeq in un estrattore potrebbe fare quasi tutto.Terzo, se le classi dei casi ereditano da una classe base sigillata, il compilatore Scala controllerà il nostro modello partite per esaustività e sarà lamentarsi se una combinazione di possibili valori non è coperta da unoModello. Nessuna di tali controlli per esaustività sono disponibili per estrattori."

che dice a me che i due sono più diverso di quanto ci si aspetterebbe a prima vista, ma senza essere specifiche su quali siano le differenze esatte sono.