Depuis Scala 2.7.2 il y a quelque chose appelé Manifest
qui est une solution de contournement pour l'effacement de type Java. Mais comment Manifest
fonctionne-t-il exactement et pourquoi/quand voulez-vous besoin de l'utiliser?
Le billet de blog Manifests: Reified Types de Jorge Ortiz en explique une partie, mais n'explique pas comment l'utiliser. avec limites du contexte .
De plus, qu'est-ce que ClassManifest
, quelle est la différence avec Manifest
?
J'ai du code (faisant partie d'un programme plus vaste, je ne peux pas l'inclure facilement ici) qui contient des avertissements concernant l'effacement de type; Je soupçonne que je peux résoudre ces problèmes en utilisant des manifestes, mais je ne sais pas exactement comment.
Le compilateur connaît plus d'informations sur les types que le runtime JVM peut facilement représenter. Un manifeste est un moyen pour le compilateur d'envoyer un message inter-dimensionnel au code au moment de l'exécution concernant les informations de type qui ont été perdues.
Ceci est similaire à la façon dont les Kleptoniens ont laissé des messages encodés dans les enregistrements fossiles et l'ADN "indésirable" des humains. En raison des limitations de la vitesse de la lumière et des champs de résonance gravitationnelle, ils ne peuvent pas communiquer directement. Mais, si vous savez vous brancher sur leur signal, vous pouvez bénéficier de façons que vous ne pouvez pas imaginer, de décider quoi manger pour le déjeuner ou quel numéro de loto jouer.
Il n'est pas clair si un manifeste bénéficierait des erreurs que vous voyez sans en savoir plus.
Une utilisation courante des manifestes consiste à faire en sorte que votre code se comporte différemment en fonction du type statique d'une collection. Par exemple, que faire si vous vouliez traiter une liste [chaîne] différemment des autres types de liste:
def foo[T](x: List[T])(implicit m: Manifest[T]) = {
if (m <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
foo(List("one", "two")) // Hey, this list is full of strings
foo(List(1, 2)) // Non-stringy list
foo(List("one", 2)) // Non-stringy list
Une solution fondée sur la réflexion à ce problème impliquerait probablement d'inspecter chaque élément de la liste.
Un contexte lié semble le plus adapté à l'utilisation de classes de types dans scala, et est bien expliqué ici par Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here -i.html
Les limites de contexte peuvent également rendre les signatures de méthode plus lisibles. Par exemple, la fonction ci-dessus pourrait être réécrite en utilisant des limites de contexte comme ceci:
def foo[T: Manifest](x: List[T]) = {
if (manifest[T] <:< manifest[String])
println("Hey, this list is full of strings")
else
println("Non-stringy list")
}
Pas une réponse complète, mais concernant la différence entre Manifest
et ClassManifest
, vous pouvez trouver un exemple dans le Scala 2.8 Array
paper :
La seule question qui reste est de savoir comment implémenter la création d'un tableau générique. Contrairement à Java, Scala permet une création d'instance nouvelle
Array[T]
oùT
est un paramètre de type. Comment cela peut-il être mis en œuvre, étant donné qu'il n'existe pas de représentation de tableau uniforme en Java?La seule façon de procéder consiste à exiger des informations d'exécution supplémentaires qui décrivent le type
T
. Scala 2.8 a un nouveau mécanisme pour cela, qui est appelé un manifeste . Un objet de typeManifest[T]
fournit des informations complètes sur le typeT
.Manifest
les valeurs sont généralement passées dans des paramètres implicites; et le compilateur sait comment les construire pour les types connus statiquementT
.Il existe également une forme plus faible nommée
ClassManifest
qui peut être construite en ne connaissant que le top- classe de niveau d'un type, sans nécessairement connaître tous ses types d'arguments .
C'est ce type d'informations d'exécution qui est requis pour la création de la baie.
Exemple:
Il faut fournir ces informations en passant un
ClassManifest[T]
dans la méthode en tant que paramètre implicite:
def tabulate[T](len:Int, f:Int=>T)(implicit m:ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
En tant que forme abrégée, un contexte bound1 peut être utilisé à la place sur le paramètre de type
T
,
(Voir ceci question SO pour illustration )
, donnant:
def tabulate[T: ClassManifest](len:Int, f:Int=>T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
Lors de l'appel de tabulation sur un type tel que
Int
, ouString
, ouList[T]
, le compilateur Scala peut créer un manifeste de classe à passer comme argument implicite à tabuler.
Un manifeste était destiné à réifier les types génériques qui sont effacés pour s'exécuter sur la machine virtuelle Java (qui ne prend pas en charge les génériques). Cependant, ils avaient de sérieux problèmes: ils étaient trop simplistes et n'étaient pas en mesure de prendre pleinement en charge le système de type Scala. Ils étaient donc obsolètes dans Scala 2.10, et sont remplacés par TypeTag
s (qui sont essentiellement ce que le compilateur Scala lui-même utilise pour représenter les types, et prend donc entièrement en charge les types Scala). Pour plus de détails sur la différence, voir:
quand en as-tu besoin?
Avant le 2013-01-04, lorsque Scala 2.10 a été publié .
Supprimons également manifest
dans scala
sources (Manifest.scala
), Nous voyons:
Manifest.scala:
def manifest[T](implicit m: Manifest[T]) = m
Donc, en ce qui concerne l'exemple de code suivant:
def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
if (m <:< manifest[String]) {
"its a string"
} else {
"its not a string"
}
}
nous pouvons voir que le manifest
function
recherche un m: Manifest[T]
implicite qui satisfait le type parameter
que vous fournissez dans notre exemple de code c'était manifest[String]
. Donc, quand vous appelez quelque chose comme:
if (m <:< manifest[String]) {
vous vérifiez si le implicit m
actuel que vous avez défini dans votre fonction est de type manifest[String]
et comme manifest
est une fonction de type manifest[T]
, il cherchera pour un manifest[String]
spécifique et il trouverait s'il y avait un tel implicite.