是否可以根据Scala中的另一个清单定义清单?
我几乎不相信这是不可能的,因为Scala Manifest信息并非打算动态使用。
这是问题所在。我有一个函数,可以返回多种类型的对象(String,Int,List [Int],List [List [String]]等)。为了支持这些多种类型,返回类型设置为Any,但是由于要键入擦除,列表,地图等支持的类型信息将丢失。为了恢复一些细节,我将返回Manifest以及返回类型。
然而,返回的信息可以放在另一个列表或地图中,然后从另一个函数返回。我想更新清单,以包括类型现在是清单定义的先前类型的List或Map这一事实。
这是一些示例代码
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]]
答案 0 :(得分:5)
编辑: Adriaan Moors正确地指出了这项工作:
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]]
。
答案 1 :(得分:5)
请注意,Scala的类型系统比退回Any
做得更好。有几种方法可以定义类型联合(a.k.a。“析取类型”)。参见例如
当然你也可以拥有自己的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具有正确的类型,尽管我们没有提到它。我很确定通过使用隐式魔术和/或特殊提取器可以简化提取过程。
答案 2 :(得分:1)
我认为因为你的函数返回Manifest [_],编译器丢失了恢复类型所需的信息。如果m
的类型为Manifest[List[Int]]
,那将是一个不同的故事。