Che cos'è un manifest in Scala e quando ne hai bisogno?


133

Dalla Scala 2.7.2 esiste qualcosa chiamato Manifestche è una soluzione alternativa per la cancellazione del tipo di Java. Ma come Manifestfunziona esattamente e perché / quando è necessario utilizzarlo?

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

Inoltre, qual è ClassManifest, qual è la differenza Manifest?

Ho un po 'di codice (parte di un programma più grande, non posso facilmente includerlo qui) che contiene alcuni avvertimenti riguardo alla cancellazione del tipo; Sospetto di poterli risolvere usando manifest, ma non sono sicuro di come.


2
C'è stata una discussione sulla mailing list sulla differenza Manifest / ClassManifest, vedi scala-programming-language.1934581.n4.nabble.com/…
Arjan Blokzijl

Risposte:


198

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

Questo è simile a come i Kleptoniani hanno lasciato messaggi codificati nei reperti fossili e nel DNA "spazzatura" degli umani. A causa delle limitazioni della velocità della luce e dei campi di risonanza gravitazionale, non sono in grado di comunicare direttamente. Ma, se sai sintonizzarti sul loro segnale, puoi trarre beneficio da modi che non riesci a immaginare, dal decidere cosa mangiare a pranzo o quale numero di lotto giocare.

Non è chiaro se un Manifest trarrebbe beneficio dagli errori che stai vedendo senza conoscere maggiori dettagli.

Un uso comune di Manifests è far sì che il codice si comporti in modo diverso in base al tipo statico di una raccolta. Ad esempio, cosa succede se si desidera trattare un Elenco [String] 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 probabilmente implicherebbe l'ispezione di ciascun elemento dell'elenco.

Un limite di contesto sembra più adatto all'uso delle classi di tipi in scala, ed è ben spiegato qui da Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

I limiti di contesto possono anche solo rendere più leggibili le firme del metodo. Ad esempio, la funzione sopra potrebbe essere riscritta usando i limiti di 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")
  }

25

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

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

L'unico modo per farlo è richiedere ulteriori informazioni di runtime che descrivono il tipo T. Scala 2.8 ha un nuovo meccanismo per questo, che si chiama Manifest . Un oggetto di tipo Manifest[T]fornisce informazioni complete sul tipo T.
Manifesti valori vengono in genere passati in parametri impliciti; e il compilatore sa come costruirli per tipi staticamente noti T.

Esiste anche una forma più debole denominata ClassManifestche può essere costruita conoscendo solo la classe di livello superiore di un tipo, senza necessariamente conoscere tutti i suoi tipi di argomento .
È questo tipo di informazioni di runtime richieste per la creazione di array.

Esempio:

È necessario fornire queste informazioni passando a 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 forma abbreviata, invece, è possibile utilizzare un limite di contesto1 sul parametro type T,

(Vedi questa domanda SO per l'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 chiama tabulate su un tipo come Int, o String, o List[T], il compilatore Scala può creare un manifest di classe da passare come argomento implicito da tabulare.


25

Un Manifest doveva reificare i tipi generici che vengono cancellati dal tipo per essere eseguiti sulla JVM (che non supporta i generici). Tuttavia, avevano 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 sostituiti da TypeTags (che sono essenzialmente ciò che il compilatore Scala stesso utilizza per rappresentare i tipi e quindi supportano completamente i tipi di Scala). Per maggiori dettagli sulla differenza, vedere:

In altre parole

quando ne hai bisogno?

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


Non è ancora deprecato (ma lo sarà), poiché la riflessione Scala è ancora sperimentale in 2.10.
Keros,

Prima del 2013-01-04 o se stai utilizzando un'API che si basa su di essa.
David Moles,

1

Chck out anche manifestin scalafonti ( 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 è stata la manifest functionricerca di un implicito m: Manifest[T]che soddisfa la type parametervostra fornitura nel nostro codice di esempio manifest[String]. Quindi quando chiami qualcosa del tipo:

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

stai verificando se la corrente implicit mche hai definito nella tua funzione è di tipo manifest[String]e poiché manifestè una funzione di tipo manifest[T]cercherebbe uno specifico manifest[String]e troverebbe se c'è un tale implicito.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.