Cos’è un Manifest in Scala e quando ne hai bisogno?

Dal momento che Scala 2.7.2 esiste qualcosa chiamato Manifest che è una soluzione alternativa per la cancellazione del tipo di Java. Ma come funziona Manifest esattamente e perché / quando è necessario utilizzarlo?

Il blog post Manifesti: Reified Types di Jorge Ortiz ne spiega alcuni, ma non spiega come usarlo insieme ai limiti del contesto .

Inoltre, cos’è ClassManifest , qual è la differenza con Manifest ?

Ho del codice (parte di un programma più grande, non posso facilmente includerlo qui) che ha alcuni avvertimenti riguardo alla cancellazione del tipo; Sospetto di poterli risolvere utilizzando i manifesti, ma non so esattamente come.

Il compilatore conosce più informazioni sui tipi che il runtime JVM può facilmente rappresentare. Un manifesto è un modo per il compilatore di inviare un messaggio interdimensionale al codice in fase di runtime in merito alle informazioni sul tipo che sono state perse.

Questo è simile al modo in cui i kleptoniani hanno lasciato messaggi codificati nei registri fossili e nel DNA “spazzatura” degli umani. A causa delle limitazioni dei campi di velocità della luce e risonanza gravitazionale, non sono in grado di comunicare direttamente. Ma, se sai come sintonizzare il loro segnale, puoi trarre beneficio in modi che non puoi immaginare, dal decidere cosa mangiare per il pranzo o quale numero del lotto giocare.

Non è chiaro se un Manifesto possa avvantaggiare gli errori che stai vedendo senza conoscere più dettagli.

Un uso comune di Manifests è fare in modo che il codice si comporti diversamente in base al tipo statico di una raccolta. Ad esempio, cosa succede se si desidera trattare un elenco [stringa] in modo diverso dagli altri tipi di un elenco:

  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 soluzione basata sulla riflessione di questo probabilmente implicherebbe l'ispezione di ogni elemento della lista.

Un context bound sembra essere più adatto all'utilizzo di classi di tipi in scala, ed è ben spiegato qui da Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classs-here-i.html

I limiti del contesto possono anche rendere più leggibili le firme del metodo. Ad esempio, la funzione di cui sopra potrebbe essere riscritta utilizzando i limiti del contesto in questo modo:

  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") } 

Non una risposta completa, ma per quanto riguarda la differenza tra Manifest e ClassManifest , puoi trovare un esempio nel documento Array Scala 2.8 :

L’unica domanda rimanente è come implementare la creazione di array generici. A differenza di Java, Scala consente la creazione di un’istanza come nuova Array[T] cui T è un parametro di tipo. Come può essere implementato, dato che non esiste una rappresentazione di matrice uniforms in Java?

L’unico modo per fare ciò è richiedere informazioni di runtime aggiuntive che descrivano il tipo T Scala 2.8 ha un nuovo meccanismo per questo, che si chiama Manifest . Un object di tipo Manifest[T] fornisce informazioni complete sul tipo T
Manifest valori Manifest vengono generalmente passati in parametri impliciti; e il compilatore sa come costruirli per tipi noti staticamente T

Esiste anche una forma più debole denominata ClassManifest che può essere costruita conoscendo solo la class di primo livello di un tipo, senza necessariamente conoscere tutti i suoi tipi di argomenti .
È questo tipo di informazioni di runtime necessarie per la creazione di array.

Esempio:

È necessario fornire queste informazioni passando ClassManifest[T] nel metodo come parametro implicito:

 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 } 

Come una forma abbreviata, un contesto bound1 può essere utilizzato sul parametro di tipo T , invece,

(Vedi questa domanda SO per illustrazione )

, 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 } 

Quando si chiama tabulate su un tipo come Int , o String , o List[T] , il compilatore Scala può creare un manifest di class da passare come argomento implicito a tabulate.

Un manifesto intendeva reificare i tipi generici che ottengono il tipo cancellato da eseguire sulla JVM (che non supporta i generici). Tuttavia, hanno avuto alcuni problemi seri: erano troppo semplicistici e non erano in grado di supportare pienamente il sistema di tipi di Scala. Sono stati quindi deprecati in Scala 2.10 e sono stati sostituiti con TypeTag s (che sono essenzialmente ciò che il compilatore Scala utilizza per rappresentare i tipi e quindi supportano completamente i tipi Scala). Per maggiori dettagli sulla differenza, vedere:

  • Scala: Che cos’è un TypeTag e come lo uso?
  • In che modo i nuovi TypeTag Scala migliorano i manifesti (deprecati)?

In altre parole

quando ne hai bisogno?

Prima del 2013-01-04, quando è stato rilasciato Scala 2.10 .

Esaminiamo anche manifest in fonti di scala ( Manifest.scala ), vediamo:

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

Quindi per quanto riguarda il seguente codice di esempio:

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

possiamo vedere che la function manifest cerca un implicito m: Manifest[T] che soddisfa il type parameter che fornisci nel nostro codice di esempio che era manifest[String] . Quindi quando chiami qualcosa come:

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

stai verificando se l' implicit m corrente implicit m che hai definito nella tua funzione è di tipo manifest[String] e poiché manifest è una funzione di tipo manifest[T] cercherebbe un manifest[String] specifico manifest[String] e troverà se ci è così implicito.