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

114

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

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

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

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

Теги:
manifest

4 ответа

178
Лучший ответ

Компилятор знает больше информации о типах, чем может просто представлять среда выполнения 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")
  }
  • 14
    Проголосовал за вариант использования. Жаль, что у меня не было второго голоса за Клептонский участок вне поля.
  • 9
    Ага, это секретное межпространственное сообщение от компилятора Scala ... ;-)
Показать ещё 2 комментария
25

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

20

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

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

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

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню