是否可以根据Scala中的另一个清单定义清单?

时间:2011-07-04 05:53:06

标签: scala manifest

是否可以根据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]]

3 个答案:

答案 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]],那将是一个不同的故事。