web-dev-qa-db-fra.com

Qu'est-ce qu'un manifeste dans Scala et quand en avez-vous besoin?

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.

130
Jesper

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")
  }
194
Mitch Blevins

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]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 type Manifest[T] fournit des informations complètes sur le type T.
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 statiquement T.

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, ou String, ou List[T], le compilateur Scala peut créer un manifeste de classe à passer comme argument implicite à tabuler.

25
VonC

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 TypeTags (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:

En d'autres termes

quand en as-tu besoin?

Avant le 2013-01-04, lorsque Scala 2.10 a été publié .

23
Mechanical snail

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 manifestfunction 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.

1
Tomer Ben David