¿Qué es un manifiesto en Scala y cuándo lo necesita?

132

Desde Scala 2.7.2 hay algo llamado Manifest que es una solución para el borrado de tipo Java. Pero, ¿cómo Manifestfunciona exactamente y por qué / cuándo necesita usarlo?

La publicación de blog Manifiestos: Tipos Reificados de Jorge Ortiz explica algo de esto, pero no explica cómo usarlo junto con los límites de contexto .

Además, ¿cuál es ClassManifest, cuál es la diferencia Manifest?

Tengo un código (parte de un programa más grande, no puedo incluirlo fácilmente aquí) que tiene algunas advertencias con respecto a la eliminación de tipo; Sospecho que puedo resolverlos usando manifiestos, pero no estoy seguro exactamente cómo.

Jesper
fuente
2
Ha habido una discusión en la lista de correo sobre la diferencia de Manifiesto / Manifiesto de clase, ver scala-programming-language.1934581.n4.nabble.com/…
Arjan Blokzijl

Respuestas:

197

El compilador conoce más información sobre los tipos que el tiempo de ejecución de JVM puede representar fácilmente. Un manifiesto es una forma para que el compilador envíe un mensaje interdimensional al código en tiempo de ejecución sobre la información de tipo que se perdió.

Esto es similar a cómo los cleptonianos han dejado mensajes codificados en los registros fósiles y el ADN "basura" de los humanos. Debido a las limitaciones de la velocidad de la luz y los campos de resonancia gravitacional, no pueden comunicarse directamente. Pero, si sabe cómo sintonizar su señal, puede beneficiarse de maneras que no puede imaginar, al decidir qué comer para el almuerzo o qué número de lotería jugar.

No está claro si un Manifiesto beneficiaría los errores que está viendo sin conocer más detalles.

Un uso común de los manifiestos es hacer que su código se comporte de manera diferente según el tipo estático de una colección. Por ejemplo, ¿qué pasaría si quisiera tratar una Lista [Cadena] de manera diferente a otros tipos de Lista:

 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

Una solución basada en la reflexión para esto probablemente implicaría inspeccionar cada elemento de la lista.

Un enlace de contexto parece más adecuado para usar clases de tipos en scala, y Debasish Ghosh lo explica bien aquí: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

Los límites de contexto también pueden hacer que las firmas de métodos sean más legibles. Por ejemplo, la función anterior podría reescribirse usando límites de contexto de la siguiente manera:

  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
fuente
25

No es una respuesta completa, pero en cuanto a la diferencia entre Manifeste ClassManifest, se puede encontrar un ejemplo en el 2,8 Scala Arrayde papel :

La única pregunta que queda es cómo implementar la creación genérica de matrices. A diferencia de Java, Scala permite una creación de instancia nueva Array[T]donde Tes un parámetro de tipo. ¿Cómo se puede implementar esto, dado el hecho de que no existe una representación de matriz uniforme en Java?

La única forma de hacerlo es requerir información adicional de tiempo de ejecución que describa el tipo T. Scala 2.8 tiene un nuevo mecanismo para esto, que se llama Manifiesto . Un objeto de tipo Manifest[T]proporciona información completa sobre el tipo T.
Manifestlos valores se pasan típicamente en parámetros implícitos; y el compilador sabe cómo construirlos para tipos conocidos estáticamenteT .

Existe también una forma más débil llamada ClassManifestque se puede construir a partir de conocer solo la clase de nivel superior de un tipo, sin conocer necesariamente todos sus tipos de argumentos .
Es este tipo de información de tiempo de ejecución lo que se requiere para la creación de matrices.

Ejemplo:

Es necesario proporcionar esta información pasando un ClassManifest[T]método al método como parámetro implícito:

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 
} 

Como forma abreviada, en su lugar, se puede usar un contexto obligado1 en el parámetro tipo T,

(Vea esta pregunta SO para ilustración )

, dando:

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 
} 

Al llamar a tabular en un tipo como Int, o String, o List[T], el compilador de Scala puede crear un manifiesto de clase para pasar como argumento implícito a tabular.

VonC
fuente
25

Un manifiesto estaba destinado a reificar tipos genéricos que se borran para ejecutarse en la JVM (que no admite genéricos). Sin embargo, tenían algunos problemas serios: eran demasiado simplistas y no podían soportar completamente el sistema de tipos de Scala. Por lo tanto, quedaron en desuso en Scala 2.10, y se reemplazan con TypeTags (que es esencialmente lo que el compilador Scala usa para representar tipos y, por lo tanto, es totalmente compatible con los tipos Scala). Para más detalles sobre la diferencia, vea:

En otras palabras

¿cuando lo necesitas?

Antes del 2013-01-04, cuando se lanzó Scala 2.10 .

Caracol mecánico
fuente
Todavía no está en desuso (pero lo estará), ya que la reflexión de Scala todavía es experimental en 2.10.
Keros
Antes del 2013-01-04, o si está utilizando una API que se basa en ella.
David Moles
1

Supongamos también chck a cabo manifesten scalafuentes ( Manifest.scala), vemos:

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

Entonces, con respecto al siguiente código de ejemplo:

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

podemos ver que las manifest functionbúsquedas de un implícito m: Manifest[T]que satisface lo que type parameterusted proporciona en nuestro código de ejemplo fue manifest[String]. Entonces cuando llamas a algo como:

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

está comprobando si la corriente implicit mque definió en su función es de tipo manifest[String]y, como manifestes una función de tipo manifest[T], buscaría un específico manifest[String]y buscaría si existe tal implícito.

Tomer Ben David
fuente