Що таке маніфест у Scala і коли він вам потрібен?


132

Оскільки у Scala 2.7.2 існує щось, що називається, Manifestщо є вирішенням стирання типу Java. Але як Manifestсаме працює і чому / коли потрібно її використовувати?

Повідомлення в блозі " Маніфести: Рефіковані типи " Хорхе Ортіса пояснює деякі з них, але не пояснює, як їх використовувати разом із рамками контексту .

Також, що таке ClassManifest, в чому різниця Manifest?

У мене є якийсь код (частина більшої програми, не можу легко включити його сюди), який має деякі попередження щодо стирання типу; Я підозрюю, що можу це вирішити, використовуючи маніфести, але я не знаю, як саме.


2
У списку розсилки відбулася дискусія про різницю Manifest / ClassManifest, див. Scala-programming-language.1934581.n4.nabble.com/…
Arjan Blokzijl

Відповіді:


197

Компілятор знає більше інформації про типи, ніж час виконання JVM може легко представити. Маніфест - це спосіб компілятора надсилати міжвимірне повідомлення до коду під час виконання інформації про інформацію про тип, яка була втрачена.

Це схоже на те, як клептоняни залишили закодовані повідомлення у копальнях викопних робіт і "мотлох" ДНК людей. Через обмеження швидкості світла та гравітаційного резонансного поля вони не можуть безпосередньо спілкуватися. Але, якщо ви знаєте, як налаштувати їх сигнал, ви можете отримати користь способами, які не уявляєте, вирішуючи, що їсти на обід або який номер лото грати.

Не ясно, чи маніфест виграє від помилок, які ви бачите, не знаючи більше деталей.

Одне поширене використання маніфестів полягає в тому, щоб ваш код поводився по-різному на основі статичного типу колекції. Наприклад, що робити, якщо ви хочете розглянути список [рядка] по-різному від інших типів списку:

 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

Рішення цього на основі роздумів, ймовірно, передбачає перевірку кожного елемента списку.

Зв'язаний контекст, здається, найбільше підходить для використання типів класів у масштабі, і тут добре пояснюється Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

Межі контексту також можуть просто зробити підписи методу більш читабельними. Наприклад, вищевказану функцію можна переписати, використовуючи такі контекстні межі:

  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

Не повна відповідь, але щодо різниці між Manifestі ClassManifest, ви можете знайти приклад у папері Scala 2.8Array :

Залишилося лише питання, як реалізувати створення загального масиву. На відміну від Java, Scala дозволяє створювати екземпляр новий, Array[T]де Tє параметром типу. Як це можна реалізувати, враховуючи той факт, що на Java не існує єдиного представлення масиву?

Єдиний спосіб зробити це - вимагати додаткової інформації про час виконання, яка описує тип T. Scala 2.8 має для цього новий механізм, який називається маніфестом . Об'єкт типу Manifest[T]надає повну інформацію про тип T.
Manifestзначення зазвичай передаються в неявних параметрах; і компілятор знає, як їх побудувати для статично відомих типів T.

Існує також слабша форма, названа ClassManifestякою, яку можна побудувати з знання лише класу верхнього рівня типу, не обов'язково знаючи всі його типи аргументів .
Саме цей тип інформації для виконання необхідний для створення масиву.

Приклад:

Потрібно надати цю інформацію, передавши ClassManifest[T]метод як неявний параметр:

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 
} 

В якості скороченої форми замість контексту1 може бути використаний параметр типу T,

(Дивіться це питання ТА для ілюстрації )

, даючи:

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 
} 

Під час виклику табуляції такого типу, як Int, або String, або List[T], компілятор Scala може створити маніфест класу, який передасть таємний аргумент в якості неявного аргументу.


25

Маніфест мав на меті реіфікувати загальні типи, які стираються для запуску на JVM (який не підтримує дженерики). Однак у них були деякі серйозні проблеми: вони були надто спрощеними та не змогли повністю підтримати типову систему Scala. Таким чином, вони застаріли в Scala 2.10 і замінені на TypeTags (що по суті те, що сам компілятор Scala використовує для представлення типів, і тому повністю підтримує типи Scala). Детальніше про різницю див. У статті:

Іншими словами

коли вам це потрібно?

До 2013-01-04, коли було випущено Scala 2.10 .


Це ще не застаріло (але буде), оскільки відображення Скали все ще експериментальне в 2.10.
Керос

До 2013-01-04 або якщо ви використовуєте API, який покладається на нього.
Девід Молес

1

Давайте також переглянемо manifestу scalaджерелах ( Manifest.scala), ми бачимо:

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

Отже, стосовно наступного прикладу коду:

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

ми можемо побачити, що manifest functionпошук неявного, m: Manifest[T]який задовольняє наданому type parameterвами коду в нашому прикладі, був manifest[String]. Тож коли ви телефонуєте щось на кшталт:

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

ви перевіряєте, чи є течія, implicit mяку ви визначили у своїй функції, типу manifest[String]і як manifestфункція типу, manifest[T]вона шукала б конкретний, manifest[String]і виявила б, чи є така неявна.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.