Scala: cas classe unapply vs une mise en œuvre manuelle et type erasure
j'essaie de comprendre ce que Scala fait avec les classes de CAs qui les rendent en quelque sorte immunisés aux avertissements d'effacement de type.
disons que nous avons la structure de classe simple suivante. C'est en gros 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]
Et vous essayez de l'utiliser comme ceci:
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 ) )
}
tout se compile et fonctionne sans aucun problème. Cependant, lorsque j'essaie de mettre en œuvre le unapply
méthode moi - même, le compilateur lance un avertissement. J'ai utilisé la classe suivante structure avec la même Main
classe ci-dessus:
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]
compilant cela avec le -unchecked
drapeau questions de l'avertissement suivant:
[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
maintenant, je comprends le type erasure et j'ai essayé de contourner l'avertissement avec <!-Mais quelle est la différence entre les deux implémentations? Est-ce que les cours de cas font quelque chose que j'ai besoin d'ajouter? Cela peut-il être contourné avec Manifests
?
j'ai même essayé de gérer la classe de cas mise en œuvre par le compilateur scala avec le -Xprint:typer
drapeau activé, mais le unapply
méthode ressemble pas mal je m'attendais:
case <synthetic> def unapply[A >: Nothing <: Any, B >: Nothing <: Any](x: $iw.$iw.White[A,B]): Option[B] = if (x.==(null))
scala.this.None
else
scala.Some.apply[B](x.right);
Merci d'avance
2 réponses
je ne peux pas donner une réponse complète, mais je peux vous dire que même si le compilateur génère un unapply
méthode pour les classes de cas, quand le motif correspond à une classe de cas, il n'utilise pas cette méthode inapply. Si vous essayez -Ybrowse:typer
en utilisant à la fois l'appariement des cas et votre unapply
méthode, vous verrez qu'un arbre de syntaxe très différent est produit (pour le match
) selon ce qui est utilisé. Vous pouvez également parcourir les phases suivantes et voir que la différence reste.
Pourquoi Scala Je ne suis pas sûr, bien que ce soit peut-être pour la raison que vous évoquez. Et comment la contourner pour votre propre unapply
je n'ai aucune idée. Mais c'est la raison pour Scala semble magiquement éviter le problème.
après expérimentation, apparemment cette version de unapply
fonctionne, mais je suis un peu confus au sujet de pourquoi:
def unapply[A,B](value: BlackOrWhite[A,B]): Option[B] = value match {
case w: White[_,_] => Some(w.right)
case _ => None
}
La difficulté avec votre unapply
c'est qu'en quelque sorte que le compilateur doit être convaincu que si un White[A,B]
étend un BlackOrWhite[C,D]
puis B
est le même que D
, que le compilateur est apparemment capable de comprendre dans cette version mais pas dans la vôtre. Je ne sais pas pourquoi.
Je ne peux pas vous donner la réponse sur la différence entre correspondance de classe de cas et non apply. Cependant, dans leur livre (Odersky, Cuillère, Venners) "Programming in Scala" 2ème chptr 26.6 "Extracteurs rapport à des classes de cas", ils écrivent:
" ils (classes de cas) conduisent généralement à des correspondances de motifs plus efficaces que les extracteurs, parce que le compilateur Scala peut optimiser les modèles sur classes de cas beaucoup mieux que les modèles sur les extracteurs. C'est parce que les mécanismes de cas les classes sont fixes, tandis qu'un délettrer ou la méthode unapplySeq dans un extracteur pourrait faire presque n'importe quoi. Troisième, si vos classes de cas héritent d'une classe de base scellée, le Scala compilateur de vérifier notre modèle correspond pour l'exhaustivité et se plaindre si certaines combinaisons de valeurs possibles n'est pas couverte par un modèle. Il n'existe pas de telles vérifications de l'exhaustivité pour les extracteurs."
ce qui me dit que les deux sont plus différents qu'on pourrait s'y attendre au début coup d'œil, cependant sans être précis sur ce que les différences exactes sont.