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