¿Qué es un Manifiesto en Scala y cuándo lo necesitas?
manifest (4)
El compilador conoce más información acerca de los tipos que el tiempo de ejecución JVM puede representar fácilmente. Un Manifiesto es una forma de 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 Kleptonianos 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, desde decidir qué comer para el almuerzo o qué número de lotería jugar.
No está claro si un Manifiesto beneficiará los errores que está viendo sin conocer más detalles.
Un uso común de Manifests es hacer que su código se comporte de manera diferente en función del 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 una 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 a esto probablemente implicaría inspeccionar cada elemento de la lista.
Un límite de contexto parece ser el más adecuado para usar clases de tipo 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 volver a escribirse utilizando los 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")
}
Desde Scala 2.7.2 hay algo llamado Manifest
que es una solución para el borrado de tipos de Java. Pero, ¿cómo funciona el Manifest
exactamente y por qué / cuándo necesita usarlo?
La publicación del blog Manifiestos: Tipos reificados por Jorge Ortiz explica algo de esto, pero no explica cómo usarlo junto con los límites de contexto .
Además, ¿qué es ClassManifest
, cuál es la diferencia con Manifest
?
Tengo un código (parte de un programa más grande, no se puede incluir fácilmente aquí) que tiene algunas advertencias con respecto a la eliminación del tipo; Sospecho que puedo resolver estos mediante el uso de manifiestos, pero no estoy seguro exactamente cómo.
No es una respuesta completa, pero con respecto a la diferencia entre Manifest
y ClassManifest
, puede encontrar un ejemplo en el documento Scala 2.8 Array
:
La única pregunta restante es cómo implementar una matriz genérica. A diferencia de Java, Scala permite una instancia que crea una nueva
Array[T]
dondeT
es 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 exigir información de tiempo de ejecución adicional que describa el tipo
T
Scala 2.8 tiene un nuevo mecanismo para esto, que se llama Manifiesto . Un objeto de tipoManifest[T]
proporciona información completa sobre el tipoT
Manifest
valores deManifest
generalmente se pasan en parámetros implícitos; y el compilador sabe cómo construirlos para los tiposT
conocidos estadísticamente.También existe una forma más débil llamada
ClassManifest
que se puede construir conociendo solo la clase de primer nivel 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]
en el método como un 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 una forma abreviada, un contexto bound1 se puede utilizar en el parámetro de tipo
T
lugar,
(Ver esta pregunta SO para la 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 tabulate en un tipo como
Int
, oString
, oList[T]
, el compilador de Scala puede crear un manifiesto de clase para pasar como argumento implícito a tabular.
También vamos a manifest
en las fuentes de scala
( 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 la function
manifest
busca un m: Manifest[T]
implícito que satisface el type parameter
usted proporciona en nuestro código de ejemplo que fue manifest[String]
. Entonces cuando llamas algo así:
if (m <:< manifest[String]) {
está comprobando si la implicit m
actual que definió en su función es de tipo manifest[String]
y como el manifest
es una función de tipo manifest[T]
buscaría un manifest[String]
específico manifest[String]
y encontraría si hubiera es tal implícito
Un Manifiesto tenía la intención de reificar los tipos genéricos que se borran por el tipo para que se ejecuten en la JVM (que no es compatible con los 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 obsoletos en Scala 2.10 y se reemplazan por TypeTag
s (que son esencialmente lo que el propio compilador de Scala usa para representar tipos y, por lo tanto, son completamente compatibles con los tipos de Scala). Para más detalles sobre la diferencia, ver:
- Scala: ¿Qué es un TypeTag y cómo lo uso?
- ¿Cómo mejoran los nuevos Scala TypeTags los Manifiestos (desaprobados)?
En otras palabras
¿cuando lo necesitas?
Antes de 2013-01-04, cuando se lanzó Scala 2.10 .