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

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

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

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

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

Ответы

Ответ 1

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

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

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

Одним из общих применений Manifests является то, что ваш код ведет себя по-разному на основе статического типа коллекции. Например, что, если вы хотите рассматривать 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, и это объясняется здесь 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")
  }

Ответ 2

Не полный ответ, но в отношении разницы между Manifest и ClassManifest вы можете найти пример в Scala 2.8 Array бумага:

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

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

(см. этот 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 
} 

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

Ответ 3

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

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

когда вам это нужно?

До 2013-01-04 когда был выпущен Scala 2.10.

Ответ 4

Пусть также выделяет 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], и он будет найти, если есть такое неявное.