2010-07-09 12 views
116

Od Scala 2.7.2 jest coś o nazwie Manifest, która jest obejściem dla usuwania typu Java. Ale jak dokładnie działa Manifest i dlaczego/kiedy trzeba z niego korzystać?Co to jest Manifest w Scali i kiedy go potrzebujesz?

Blog po Manifests: Reified Types Jorge Ortiz wyjaśnia niektóre z nich, ale nie wyjaśnia, jak go używać razem z context bounds.

Co to jest ClassManifest, czym się różni od Manifest?

Mam trochę kodu (część większego programu, nie można go tutaj łatwo zawrzeć), który ma pewne ostrzeżenia dotyczące usuwania typu; Podejrzewam, że mogę je rozwiązać za pomocą manifestów, ale nie jestem pewien, jak dokładnie.

+2

Na liście dyskusyjnej pojawiła się dyskusja na temat różnicy Manifest/ClassManifest, patrz http://scala-programming-language.1934581.n4.nabble.com/What-s-the-difference-between-ClassManifest- and-Manifest-td2125122.html –

+0

Zobacz też: [Scala: Co to jest TypeTag i jak tego używać?] (http://stackoverflow.com/questions/12218641/scala-what-is-a-typetag-and -how-do-i-use-it) – Jesper

Odpowiedz

174

Kompilator wie więcej informacji o typach niż można łatwo przedstawić w środowisku wykonawczym JVM. Manifest to sposób, w jaki kompilator przesyła międzywymiarowy komunikat do kodu w środowisku wykonawczym na temat utraconych informacji o typie.

Jest to podobne do tego, jak Kleptonianie pozostawili zakodowane wiadomości w zapisach kopalnych i "śmieciowe" DNA ludzi. Ze względu na ograniczenia prędkości światła i pól rezonansu grawitacyjnego nie są one w stanie komunikować się bezpośrednio. Ale jeśli wiesz, jak dostroić się do ich sygnału, możesz skorzystać w sposób, którego nie możesz sobie wyobrazić, od decydowania o tym, co zjeść na obiad lub o numerze lotto do zagrania.

Nie jest jasne, czy oczywisty skorzystają błędy widzisz, nie znając więcej szczegółów.

Jednym z typowych zastosowań Manifestów jest zachowanie kodu w różny sposób w zależności od statycznego typu kolekcji. Na przykład, co zrobić, jeśli chciał traktować listy [łańcuch] odmiennie od innych typów z listy:

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 

odbicie rozwiązanie oparte A do tego najprawdopodobniej obejmować kontrolę każdego elementu listy.

Kontekst związany wydaje się najbardziej nadaje się do używania typu zajęcia w Scala, i jest dobrze wyjaśnione tutaj Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

granice kontekstu można też po prostu zrobić podpisy metoda bardziej czytelny. Na przykład, powyższa funkcja może być ponownie napisane przy użyciu granic kontekstowych tak:

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

Wycofano dla przypadku użycia. Żałuję, że nie miałem drugiego głosowania na sekcję kleptońską poza polem. – huynhjl

+8

Aha, to tajna międzywymiarowa wiadomość od kompilatora Scala ... ;-) – Jesper

+1

Kryptonians? Wielka metafora i tak! – kirhgoff

25

Nie pełnej odpowiedzi, ale w odniesieniu do różnicy między Manifest i ClassManifest można znaleźć przykładowo w Scala 2.8 Array paper:

Jedyne pytanie brzmi, jak wdrożyć tworzenie tablicy rodzajowe. W przeciwieństwie do Java, Scala umożliwia tworzenie instancji nowej Array[T] gdzie T jest parametrem typu. W jaki sposób można to zrealizować, biorąc pod uwagę fakt, że w Javie nie istnieje jednolita reprezentacja tablic?

Jedynym sposobem wykonania tej czynności jest wymaganie dodatkowych informacji o środowisku wykonawczym, które opisują typ T. Scala 2.8 ma nowy mechanizm do tego, który nazywa się Manifest. Obiekt typu Manifest[T] zapewnia kompletne informacje na temat typu T.
Manifest wartości są zwykle przekazywane w niejawnych parametrach; a kompilator wie, jak je skonstruować dla statycznie znanych typów T.

Istnieje także słabsza forma nazwie ClassManifest która może być wykonana z wiedząc tylko klasę najwyższego poziomu typu, niekoniecznie znając wszystkie swoje typy argumentów.
Ten typ informacji o środowisku wykonawczym jest wymagany do tworzenia macierzy.

przykład:

trzeba podać tę informację przez przepuszczanie ClassManifest[T] do metody jako pośrednie parametru:

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 
} 

W skróconej postaci zapewniony context bound1 może być użyty zamiast parametru T,

(Zobacz ten SO question for illustration)

, podając:

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 
} 

Dzwoniąc tabularyzować od typu, takie jak Int lub String lub List[T], kompilator Scala can utwórz manifest klasy, który zostanie przekazany jako niejawny argument do tabeli.

22

oczywisty miało zreifikować typy generyczne, które popadły typu skasowane uruchomić na JVM (która nie obsługuje rodzajowych). Miały jednak poważne problemy: były zbyt uproszczone i nie były w stanie w pełni wesprzeć systemu typu Scala. Zostały one zatem przestarzałe w Scala 2.10, i są zastępowane przez TypeTag s (które są zasadniczo tym, co sam kompilator Scali używa do reprezentowania typów, a zatem w pełni obsługują typy Scala). Więcej informacji na temat różnicy, patrz:

Innymi słowy

gdy nie jest to potrzebne?

Przed 2013-01-04, when Scala 2.10 was released.

+0

To nie jest jeszcze przestarzałe (ale będzie), ponieważ odbicie Scala jest wciąż eksperymentalne w 2.10. – Keros

+0

Przed 2013-01-04 lub jeśli korzystasz z interfejsu API, który się na nim opiera. –

1

Załóżmy również chck się manifest w scala źródeł (Manifest.scala) widzimy:

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

Więc z odniesieniu do Poniższy przykład kodu:

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

widzimy, że manifestfunction wyszukuje niejawne m: Manifest[T], które spełnia type parameter można podać w naszym przykładzie kod był manifest[String]. Więc kiedy nazywają coś takiego:

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

jest sprawdzana, jeśli bieżąca implicit m którym zdefiniowane w funkcji jest typu manifest[String] i jako manifest jest funkcją typu manifest[T] byłoby szukać konkretnej manifest[String] i to odkryłby, czy jest taki domniemany.

Powiązane problemy