Что такое манифест в Scala и когда он вам нужен?

133

Начиная с Scala 2.7.2 существует так называемый Manifestспособ обхода стирания типов Java. Но как Manifestименно работает и зачем / когда это нужно использовать?

Сообщение в блоге Хорхе Ортиса « Manifests: Reified Types » объясняет некоторые из них, но не объясняет, как их использовать вместе с ограничениями контекста .

Кроме того ClassManifest, в чем разница Manifest?

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

Jesper
источник
2
В списке рассылки обсуждалось различие Manifest / ClassManifest, см. Scala-programming-language.1934581.n4.nabble.com/…
Arjan Blokzijl

Ответы:

198

Компилятор знает больше информации о типах, чем может легко представить среда выполнения JVM. Манифест - это способ компилятора отправить межпространственное сообщение коду во время выполнения о потерянной информации о типе.

Это похоже на то, как клептонианцы оставили закодированные сообщения в окаменелостях и «мусорной» ДНК людей. Из-за ограничений скорости света и полей гравитационного резонанса они не могут общаться напрямую. Но, если вы знаете, как настроиться на их сигнал, вы можете получить выгоду невообразимыми способами, решив, что поесть на обед или какое число в лотерею сыграть.

Неясно, принесет ли Манифест пользу ошибкам, которые вы видите, не зная более подробностей.

Одним из распространенных способов использования манифестов является изменение поведения вашего кода в зависимости от статического типа коллекции. Например, что, если вы хотите обрабатывать List [String] иначе, чем другие типы List:

 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

Решение на основе отражения, вероятно, будет включать проверку каждого элемента списка.

Ограничение контекста кажется наиболее подходящим для использования классов типов в scala, и здесь это хорошо объясняет Дебасиш Гош: 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, вы можете найти пример в 2,8 Scala Arrayбумаги :

Единственный оставшийся вопрос - как реализовать создание универсального массива. В отличие от 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 
} 

В сокращенной форме Tвместо параметра типа можно использовать context bound1 ,

(См. Этот вопрос SO для иллюстрации )

, давая:

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 может создать манифест класса для передачи в качестве неявного аргумента для табуляции.

VonC
источник
25

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

Другими словами

когда тебе это нужно?

До 04.01.2013, когда была выпущена Scala 2.10 .

Механическая улитка
источник
Это еще не устарело (но будет), поскольку отражение Scala все еще является экспериментальным в 2.10.
Keros
До 04.01.2013 или если вы используете 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]и он обнаружит, есть ли такой неявный.

Томер Бен Дэвид
источник