Что такое манифест в 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 и заменены на TypeTag
s (которые по сути то, что сам компилятор Scala использует для представления типов и, следовательно, полностью поддерживает типы Scala). Для более подробной информации о разнице, см.:
- Scala: Что такое TypeTag и как его использовать?
- Как новые Scala TypeTag улучшают (устарели) Манифесты?
Другими словами
когда тебе это нужно?
До 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"
}
}
мы можем видеть, что manifest
function
ищет неявное m: Manifest[T]
который удовлетворяет type parameter
вы предоставляете в нашем примере кода это было manifest[String]
, Поэтому, когда вы звоните что-то вроде:
if (m <:< manifest[String]) {
вы проверяете текущий implicit m
который вы определили в своей функции, имеет тип manifest[String]
и как manifest
является функцией типа manifest[T]
это будет искать конкретный manifest[String]
и было бы найти, если есть такое неявное.