【问题标题】:Is it possible to have a manifest defined based on another manifest in Scala?是否可以根据 Scala 中的另一个清单定义清单?
【发布时间】:2011-07-04 05:53:05
【问题描述】:

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

【问题讨论】:

  • 我真的很想知道是什么限制促使你使用这种奇怪的设计?

标签: scala manifest


【解决方案1】:

编辑: 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[_]]

注意虽然会返回正确的manifest,但makeListManifest的静态返回类型只有Manifest[List[_]]。但是您可以在这里安全地转换为Manifest[List[T]]

【讨论】:

  • 我认为您只是忘记了参数列表中的隐式修饰符。这对我有用:def makeListManifest[T: Manifest] = manifest[List[T]]
【解决方案2】:

请注意,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 具有正确的类型,尽管我们没有提到它。我很确定可以通过使用隐式魔法和/或特殊提取器来简化提取过程。

【讨论】:

  • 如果您能详细说明如何使用其中一个链接中描述的内容来实现联合类型作为方法的返回类型,我会很感兴趣。
  • 非常感谢!太糟糕了,模式匹配部分太冗长了。
  • 这真的很棒。关于实现联合类型的文章和解决方案一样令人难以置信。但是,我遇到的问题的实际性质是递归的。当我开始处理时,我可能知道类型(联合将有很大帮助),但运行时逻辑将确定是否创建了这些类型的列表或映射以及创建的深度。除非我预先定义了预设数量的组合,否则不能使用联合静态定义,但可以在清单中跟踪它......
【解决方案3】:

我认为因为您的函数返回 Manifest[_],编译器丢失了恢复类型所需的信息。如果mManifest[List[Int]] 类型,那就另当别论了。

【讨论】:

  • 没错,但您可以使用伴随对象上的方法自己构造清单。
猜你喜欢
  • 2020-02-27
  • 1970-01-01
  • 2015-08-16
  • 1970-01-01
  • 2011-06-26
  • 1970-01-01
  • 2016-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多