Was ist ein Manifest in Scala und wann brauchen Sie es?

132

Seit Scala 2.7.2 gibt es etwas, Manifestdas eine Problemumgehung für das Löschen von Java-Typen darstellt. Aber wie funktioniert das Manifestgenau und warum / wann müssen Sie es verwenden?

Der Blog-Beitrag Manifests: Reified Types von Jorge Ortiz erklärt einige davon, erklärt aber nicht, wie man es zusammen mit Kontextgrenzen verwendet .

Was ist ClassManifestder Unterschied Manifest?

Ich habe Code (Teil eines größeren Programms, kann hier nicht einfach eingefügt werden), der einige Warnungen in Bezug auf das Löschen des Typs enthält. Ich vermute, ich kann diese Probleme mithilfe von Manifesten lösen, bin mir aber nicht sicher, wie.

Jesper
quelle
2
Es gab eine Diskussion auf der Mailingliste über den Unterschied zwischen Manifest und ClassManifest, siehe scala-programming-language.1934581.n4.nabble.com/…
Arjan Blokzijl

Antworten:

197

Der Compiler kennt mehr Informationen über Typen, als die JVM-Laufzeit leicht darstellen kann. Ein Manifest ist eine Möglichkeit für den Compiler, zur Laufzeit eine interdimensionale Nachricht an den Code über die verlorenen Typinformationen zu senden.

Dies ähnelt der Art und Weise, wie die Kleptonier verschlüsselte Nachrichten in Fossilienbeständen und der "Junk" -DNA von Menschen hinterlassen haben. Aufgrund der Einschränkungen der Lichtgeschwindigkeit und der Gravitationsresonanzfelder können sie nicht direkt kommunizieren. Wenn Sie jedoch wissen, wie Sie das Signal einstellen können, können Sie auf unvorstellbare Weise davon profitieren, wenn Sie entscheiden, was Sie zum Mittagessen essen oder welche Lottozahl Sie spielen möchten.

Es ist nicht klar, ob ein Manifest den angezeigten Fehlern zugute kommen würde, ohne mehr Details zu wissen.

Eine häufige Verwendung von Manifests besteht darin, dass sich Ihr Code je nach statischem Typ einer Sammlung unterschiedlich verhält. Was wäre zum Beispiel, wenn Sie eine Liste [Zeichenfolge] anders als andere Listentypen behandeln möchten:

 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

Eine reflexionsbasierte Lösung hierfür würde wahrscheinlich die Überprüfung jedes Elements der Liste beinhalten.

Eine Kontextbindung scheint für die Verwendung von Typklassen in Scala am besten geeignet zu sein und wird hier von Debasish Ghosh gut erklärt: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

Kontextgrenzen können auch die Methodensignaturen besser lesbar machen. Zum Beispiel könnte die obige Funktion unter Verwendung von Kontextgrenzen wie folgt neu geschrieben werden:

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

Keine vollständige Antwort, aber in Bezug auf den Unterschied zwischen Manifestund ClassManifestfinden Sie ein Beispiel im Scala 2.8- ArrayArtikel :

Die einzige verbleibende Frage ist, wie die generische Array-Erstellung implementiert wird. Im Gegensatz zu Java ermöglicht Scala die Erstellung einer neuen Instanz, Array[T]wobei Tes sich um einen Typparameter handelt. Wie kann dies implementiert werden, da es in Java keine einheitliche Array-Darstellung gibt?

Die einzige Möglichkeit, dies zu tun, besteht darin, zusätzliche Laufzeitinformationen zu benötigen, die den Typ beschreiben T. Scala 2.8 hat hierfür einen neuen Mechanismus, der als Manifest bezeichnet wird . Ein Objekt vom Typ Manifest[T]liefert vollständige Informationen über den Typ T.
ManifestWerte werden normalerweise in impliziten Parametern übergeben. und der Compiler weiß, wie man sie für statisch bekannte Typen erstellt T.

Es gibt auch eine schwächere Form mit dem Namen, ClassManifestdie sich aus der Kenntnis der obersten Klasse eines Typs zusammensetzen lässt, ohne unbedingt alle Argumenttypen zu kennen .
Diese Art von Laufzeitinformationen ist für die Array-Erstellung erforderlich.

Beispiel:

Diese Informationen müssen bereitgestellt werden, indem ein ClassManifest[T]als impliziter Parameter an die Methode übergeben wird:

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 
} 

Als Kurzform kann Tstattdessen eine Kontextgrenze1 für den Typparameter verwendet werden.

(Siehe diese SO-Frage zur Veranschaulichung )

, geben:

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 
} 

Beim Aufrufen von tabulate für einen Typ wie Intoder oder Stringoder List[T]kann der Scala-Compiler ein Klassenmanifest erstellen, das als implizites Argument für tabulate übergeben wird.

VonC
quelle
25

Ein Manifest sollte generische Typen bestätigen, deren Typ gelöscht wird, damit sie auf der JVM ausgeführt werden können (die keine Generika unterstützt). Sie hatten jedoch einige schwerwiegende Probleme: Sie waren zu simpel und konnten das Typensystem von Scala nicht vollständig unterstützen. Sie waren so veraltet in Scala 2.10 und werden ersetzt durch TypeTags (die im Wesentlichen , was der Scala - Compiler selbst verwendet Typen darzustellen, und daher voll Scala Typen unterstützen). Weitere Einzelheiten zum Unterschied finden Sie unter:

Mit anderen Worten

Wann brauchst du es?

Vor dem 04.01.2013, als Scala 2.10 veröffentlicht wurde .

Mechanische Schnecke
quelle
Es ist noch nicht veraltet (wird es aber sein), da die Scala-Reflexion in 2.10 noch experimentell ist.
Keros
Vor dem 04.01.2013 oder wenn Sie eine API verwenden, die darauf basiert.
David Moles
1

Lassen Sie uns auch manifestin scalasources ( Manifest.scala) chicken , wir sehen:

Manifest.scala:
def manifest[T](implicit m: Manifest[T])           = m

Also in Bezug auf folgenden Beispielcode:

def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
  if (m <:< manifest[String]) {
    "its a string"
  } else {
    "its not a string"
  }
}

Wir können sehen, dass die manifest functionSuche nach einem Impliziten, m: Manifest[T]das den von type parameterIhnen in unserem Beispielcode angegebenen entspricht, es war manifest[String]. Wenn Sie also etwas anrufen wie:

if (m <:< manifest[String]) {

Sie prüfen, ob der Strom, implicit mden Sie in Ihrer Funktion definiert haben, vom Typ ist, manifest[String]und da manifestes sich um eine Funktion vom Typ handelt manifest[T], würde nach einem bestimmten gesucht manifest[String]und festgestellt, ob es einen solchen Impliziten gibt.

Tomer Ben David
quelle