什么是Scala中的清单,什么时候需要它?

时间:2010-07-09 14:21:03

标签: scala manifest

自Scala 2.7.2以来,有一些叫做Manifest的东西,它是Java类型擦除的一种解决方法。但是Manifest如何正确地工作以及为什么/何时需要使用它?

Jorge Ortiz撰写的博客文章 Manifests: Reified Types 解释了其中的一些内容,但未解释如何将其与context bounds一起使用。

此外,ClassManifest是什么,与Manifest的区别是什么?

我有一些代码(一个更大的程序的一部分,不能轻易地包含在这里),它有关于类型擦除的一些警告;我怀疑我可以通过使用清单解决这些问题,但我不确定究竟是怎么做的。

4 个答案:

答案 0 :(得分:189)

编译器知道有关类型的更多信息,而不是JVM运行时可以轻松表示的信息。清单是编译器在运行时向代码发送关于丢失的类型信息的跨维消息的一种方式。

这类似于Kleptonians如何在化石记录和人类的“垃圾”DNA中留下编码信息。由于光速和重力场的限制,它们无法直接通信。但是,如果你知道如何调整他们的信号,你可以从你无法想象的方式中受益,从决定午餐吃什么或玩多少乐透号码。

目前尚不清楚Manifest是否会在不了解更多细节的情况下使您所看到的错误受益。

Manifest的一个常见用途是根据集合的静态类型使代码的行为不同。例如,如果您希望以不同于其他类型的List的方式处理List [String],那该怎么办?

 def foo[T](x: List[T])(implicit m: Manifest[T]) = {
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  foo(List("one", "two")) // Hey, this list is full of strings
  foo(List(1, 2)) // Non-stringy list
  foo(List("one", 2)) // Non-stringy list

基于反射的解决方案可能涉及检查列表中的每个元素。

上下文绑定似乎最适合在scala中使用类型类,Debasish Ghosh在这里对此进行了很好的解释: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

上下文边界也可以使方法签名更具可读性。例如,可以使用如下的上下文边界重写上述函数:

  def foo[T: Manifest](x: List[T]) = {
    if (manifest[T] <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

答案 1 :(得分:25)

不是完整的答案,但关于ManifestClassManifest之间的区别,您可以在Scala 2.8 Array paper中找到一个示例:

  

唯一剩下的问题是如何实现通用数组创建。与Java不同,Scala允许实例创建新Array[T],其中T是类型参数。考虑到Java中不存在统一的数组表示,如何实现它?

     

执行此操作的唯一方法是需要额外的运行时信息来描述类型T。 Scala 2.8有一个新的机制,称为 Manifest Manifest[T]类型的对象提供有关类型T的完整信息   Manifest值通常在隐式参数中传递;并且编译器知道如何为静态已知类型T构造它们。

     

还存在一个名为 ClassManifest弱表单,它可以通过只知道一个类型的顶级类来构造,而不必知道它的所有参数类型
  正是这种类型的运行时信息是数组创建所必需的。

示例:

  

人们需要通过将ClassManifest[T]传递给我们来提供此信息   方法作为隐式参数:

def  tabulate[T](len:Int,  f:Int=>T)(implicit m:ClassManifest[T]) =  { 
  val  xs  =  new  Array[T](len) 
  for   (i  <- 0  until   len)  xs(i)   = f(i) 
  xs 
} 
  

作为简写形式,可以在类型参数T上使用上下文bound1,而不是

(见SO question for illustration

  

,给予:

def  tabulate[T:    ClassManifest](len:Int,  f:Int=>T)  =  { 
  val  xs  =  new  Array[T](len) 
  for   (i  <- 0  until   len)  xs(i)   = f(i) 
  xs 
} 
  

在调用IntStringList[T]等类型的表格时,Scala编译器可以创建一个类清单,作为隐式参数传递给表格。

答案 2 :(得分:23)

Manifest旨在重新定义在JVM上运行类型擦除的泛型类型(不支持泛型)。然而,他们有一些严重的问题:他们太简单了,并且无法完全支持Scala的类型系统。因此,它们在Scala 2.10中被弃用,并被TypeTag替换(这实际上是Scala编译器本身用于表示类型的内容,因此完全支持Scala类型)。有关差异的更多详细信息,请参阅:

换句话说

  

你什么时候需要它?

2013-01-04之前,when Scala 2.10 was released

答案 3 :(得分:1)

让我们在manifest来源(scala)中查看Manifest.scala,我们看到:

Manifest.scala:
def manifest[T](implicit m: Manifest[T])           = m

关于以下示例代码:

def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
  if (m <:< manifest[String]) {
    "its a string"
  } else {
    "its not a string"
  }
}

我们可以看到manifest function搜索了一个隐式m: Manifest[T],它满足您在我们的示例代码type parameter中提供的manifest[String]。所以,当你打电话时:

if (m <:< manifest[String]) {

您正在检查您在函数中定义的当前implicit m是否为manifest[String]类型,而manifestmanifest[T]类型的函数,它会搜索一个特定的manifest[String],它会发现是否存在这样的隐含。