是否可以根据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))
Run Code Online (Sandbox Code Playgroud)
从'v1'的清单我知道v1是List [Int]类型所以当我创建'v2'时,我应该拥有创建清单所需的所有信息,以识别该类型是List [List [Int]],但相反,我只有List [Any]可以使用.也许语法如下:
val v2: m = List(x)
val v2 = List[m](x)
Run Code Online (Sandbox Code Playgroud)
我意识到它看起来像是在尝试动态定义类型,但实际上信息是与静态已知类型的类型擦除相关的元数据.我想如果可以解决这个问题,那么可以解决类型擦除问题.但是,至少我认为我应该能够做到这样的事情:
scala> val m2 = m.wrapInList()
m2: scala.reflect.Manifest[_] =
scala.collection.immutable.List[scala.collection.immutable.List[Int]]
Run Code Online (Sandbox Code Playgroud)
编辑: Adriaan Moors正确指出这项工作:
def makeListManifest[T: Manifest] = manifest[List[T]]
Run Code Online (Sandbox Code Playgroud)
你只需要明确地调用它,将它传递给你已经获得的清单.
我的回答是:
huynhjl部分正确:目前这不会自动生效.我们需要编译器足够聪明来编译它:
def makeListManifest[T](m: Manifest[T]) = manifest[List[T]]
Run Code Online (Sandbox Code Playgroud)
没有任何额外的隐含参数.虽然它看起来确实可行(所有需要的信息都在这里),它还没有实现(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]
Run Code Online (Sandbox Code Playgroud)
所以你可以makeListManifest自己实现:
scala> def makeListManifest[T](m: Manifest[T]) = Manifest.classType(classOf[List[_]], m)
makeListManifest: [T](m: scala.reflect.Manifest[T])scala.reflect.Manifest[List[_]]
Run Code Online (Sandbox Code Playgroud)
请注意,虽然将返回正确的清单,但静态返回类型makeListManifest仅为Manifest[List[_]].但你可以安全地投到Manifest[List[T]]这里.
请注意,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
...
Run Code Online (Sandbox Code Playgroud)
[编辑]
根据第二个链接的定义,我们可以写:
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"))
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以详细检索但安全的类型:
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)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,匹配变量s具有正确的类型,尽管我们没有提到它.我很确定通过使用隐式魔术和/或特殊提取器可以简化提取过程.
| 归档时间: |
|
| 查看次数: |
299 次 |
| 最近记录: |