Можно ли определить манифест на основе другого манифеста в Scala? - PullRequest
3 голосов
/ 04 июля 2011

Можно ли определить манифест на основе другого манифеста в Scala?

Я почти смирился с убеждением, что это невозможно, поскольку информация о манифесте Scala не предназначена для использованиядинамически.

Вот проблема.У меня есть функция, которая может возвращать более одного типа объекта (String, Int, List [Int], List [List [String]] и т. Д.) Для поддержки этих нескольких типов тип возвращаемого значения установлен на Any, но из-задля стирания типа информация о типах, поддерживаемых в списках, картах и ​​т. д., теряется.В попытке восстановить некоторые детали я возвращаю Манифест вместе с типом возвращаемого значения.

Однако возвращаемая информация затем может быть помещена в другой список или карту и затем возвращена из другой функции.Я хочу обновить манифест, включив в него тот факт, что тип теперь является списком или картой предыдущего типа, как это определено в манифесте.

Вот пример кода

def returnWithManifest[T: Manifest](x: T) = (x, manifest[T])

// May return String, Int, List[Int], List[List[String]], ...
def contrivedExample(t: String): (Any, Manifest[_]) = t match {
  case "String" => returnWithManifest("test")
  case "Int" => returnWithManifest(1)
  case "Boolean" => returnWithManifest(true)
  case "List[Int]" => returnWithManifest(List(1,2,3))
  case "List[List[String]]" => 
    returnWithManifest(List(List("a","b"),List("c","d")))
  case _ => returnWithManifest(None)
}

scala> val v1 = contrivedExample("List[Int]")
v1: (Any, Manifest[_]) = (List(1, 2, 3),scala.collection.immutable.List[Int])

scala> val x = v1._1
x: Any = List(1, 2, 3)

scala> val m = v1._2
m: scala.reflect.Manifest[_] = scala.collection.immutable.List[Int]

scala> val v2 = List(x)
v2: List[Any] = List(List(1, 2, 3))

Из манифеста 'v1' я знаю, что v1 имеет тип List [Int], поэтому, когда я создаю 'v2', у меня должна быть вся информацияМне нужно создать манифест, определяющий, что типом является List [List [Int]], но вместо этого у меня есть только List [Any] для работы.Возможно синтаксис, подобный следующему:

val v2: m = List(x)
val v2 = List[m](x)

Я понимаю, что похоже, что я пытаюсь определить тип динамически, но на самом деле информация - это метаданные, связанные со стиранием типов статически известных типов.Я думаю, что если это можно решить, то стирание типа может быть решено.Однако, по крайней мере, я думал, что смогу сделать что-то вроде:

scala> val m2 = m.wrapInList()
m2: scala.reflect.Manifest[_] = 
      scala.collection.immutable.List[scala.collection.immutable.List[Int]]

Ответы [ 3 ]

5 голосов
/ 04 июля 2011

Редактировать: Адриан Мурс прямо указывает на это:

def makeListManifest[T: Manifest] = manifest[List[T]]

Вам просто нужно будет явно назвать его, передав ему полученный манифест.


Мой старый ответ:

huynhjl частично прав: в настоящее время это не будет работать автоматически.Нам нужно, чтобы компилятор был достаточно умен, чтобы скомпилировать это:

def makeListManifest[T](m: Manifest[T]) = manifest[List[T]]

без каких-либо дополнительных неявных параметров.Хотя это, конечно, выглядит выполнимо (вся необходимая информация здесь), оно еще не реализовано (2.9.0.1), так как я считаю, что манифесты теперь либо вставляются локально, если компилятор имеет всю информацию статического типа, в которой он нуждается, либо просматривается внеявная область действия, но не сгенерированная из других (возможно неявно доступных) манифестов.

Однако вы можете создать конструкцию, которая проявляет себя с помощью методов объекта-компаньона:

scala> import reflect.Manifest
scala> Manifest.classType(classOf[List[_]], manifest[Int])
res0: scala.reflect.Manifest[List[_]] = scala.collection.immutable.List[Int]

Таким образом, вы можете реализовать makeListManifest самостоятельно:

scala> def makeListManifest[T](m: Manifest[T]) = Manifest.classType(classOf[List[_]], m)            
makeListManifest: [T](m: scala.reflect.Manifest[T])scala.reflect.Manifest[List[_]]

Обратите внимание, что, хотя будет возвращен правильный манифест, тип статического возврата makeListManifest равен только Manifest[List[_]].Но вы могли бы спокойно привести к Manifest[List[T]] здесь.

5 голосов
/ 04 июля 2011

Обратите внимание, что система типов Scala позволяет вам делать лучше, чем возвращать Any.Есть несколько способов определить объединения типов (иначе говоря, «дизъюнктивные типы»).См., Например,

Конечно, вы также можете иметь свой собственный ADT для типа возврата, который ИМХО является самым чистым решением:

trait ReturnValue   
case class ReturnInt(value: Int) extends ReturnValue
case class ReturnString(value: String) extends ReturnValue
case class ReturnIntList(value: List[Int]) extends ReturnValue
...

[Редактировать]

Используя определение из второй ссылки, мы могли бы написать:

def contrivedExample(t: String): String or List[Int] or List[List[String]] = t match {
  case "String" => "test"
  case "List[Int]" => List(1,2,3)
  case "List[List[String]]" => List(List("a","b"),List("c","d"))
}

Теперь мы можем многословно, но безопасно получить тип:

def otherContrivedExample(t: String or List[Int] or List[List[String]]) = t match {  
  case DisjointType(Some(DisjointType(Some(s),_)), _) => println("processing String: " + s)  
  case DisjointType(Some(DisjointType(_,Some(s))), _) => println("processing List[String]: " + s)  
  case DisjointType(_,Some(s)) => println("processing List[List[Int]]: head=" + s.head)  
}

val x =  contrivedExample("List[List[String]]")
otherContrivedExample(x)
//--> processing List[List[Int]]: head=List(a, b)

Как вы можете видеть, что соответствующая переменнаяs имеет правильный тип, несмотря на то, что мы не упомянули об этом.Я уверен, что процесс извлечения может быть упрощен с помощью неявной магии и / или специальных экстракторов.

1 голос
/ 04 июля 2011

Я думаю, что поскольку ваша функция возвращает Manifest [_], компилятор потерял необходимую информацию для восстановления типа. Если бы m было типа Manifest[List[Int]], это была бы другая история.

...