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

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

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

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

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

4 ответа

Решение

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

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

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

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

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

Не полный ответ, но относительно разницы между 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 
} 

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

Давайте также проверить 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"
  }
}

мы можем видеть, что manifestfunction ищет неявное m: Manifest[T] который удовлетворяет type parameter вы предоставляете в нашем примере кода это было manifest[String], Поэтому, когда вы звоните что-то вроде:

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

вы проверяете текущий implicit m который вы определили в своей функции, имеет тип manifest[String] и как manifest является функцией типа manifest[T] это будет искать конкретный manifest[String] и было бы найти, если есть такое неявное.

Другие вопросы по тегам