Что такое манифест в Scala и когда он вам нужен? - PullRequest
129 голосов
/ 09 июля 2010

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

В блоге Манифесты: уточненные типы Хорхе Ортиса объясняются некоторые изэто, но это не объясняет, как использовать его вместе с границами контекста .

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

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

Ответы [ 4 ]

192 голосов
/ 09 июля 2010

Компилятор знает больше информации о типах, чем может легко представить среда выполнения 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 голосов
/ 09 июля 2010

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

Единственный оставшийся вопрос - как реализовать создание универсального массива. В отличие от Java, Scala позволяет создавать новый экземпляр Array[T], где T - параметр типа. Как это можно реализовать, учитывая тот факт, что в Java не существует представления единого массива?

Единственный способ сделать это - запросить дополнительную информацию времени выполнения, которая описывает тип T. В Scala 2.8 для этого есть новый механизм, который называется Manifest . Объект типа 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 можно использовать контекстную границу1,

(см. Этот ТАК вопрос для иллюстрации )

, дача:

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

23 голосов
/ 02 февраля 2013

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

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

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

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

1 голос
/ 27 февраля 2016

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

...