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


132

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

Jorge Ortiz 撰写的博客文章Manifests:Reified Types解释了其中的一些内容,但没有说明如何与上下文范围一起使用它。

还有,什么是ClassManifest什么Manifest

我有一些代码(是更大程序的一部分,在这里不能轻易包含它),其中包含有关类型擦除的警告。我怀疑我可以通过使用清单来解决这些问题,但我不确定具体如何。


2
关于清单/类清单差异的邮件列表上已有讨论,请参阅scala-programming-language.1934581.n4.nabble.com/…–
Arjan Blokzijl

Answers:


197

编译器比JVM运行时更容易表示有关类型的信息。清单是编译器在运行时向代码发送关于丢失的类型信息的跨维度消息的一种方式。

这类似于克里普托人如何将编码信息留在化石记录和人类“垃圾” DNA中。由于光速和重力共振场的限制,它们无法直接通信。但是,如果您知道如何调适他们的信号,则可以通过决定午餐吃什么或玩哪个乐透号码,以无法想象的方式受益。

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

清单的一种常见用法是使代码的行为基于集合的静态类型而有所不同。例如,如果您想将List [String]与其他类型的List区别对待:

 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")
  }

25

不是一个完整的答案,但对于之间的区别ManifestClassManifest,你可以找到一个例子斯卡拉2.8 Array

剩下的唯一问题是如何实现通用数组创建。与Java,斯卡拉允许一个实例创建新的 Array[T]地方T是一个类型参数。考虑到Java中不存在统一的数组表示形式,该如何实现?

唯一的方法就是需要描述类型的其他运行时信息T。Scala 2.8为此提供了一种新的机制,称为清单。类型的对象Manifest[T]提供有关类型的完整信息T
Manifest值通常以隐式参数传递;并且编译器知道如何为静态已知类型构造它们T

还存在一个较弱的形式命名ClassManifest,可以从了解只是顶层类的类型,而不必知道它的所有参数类型来构造
数组创建需要这种类型的运行时信息。

例:

需要通过将a 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 
} 

作为一种简写形式,可以在type参数上使用上下文bound1 T

(请参见此SO问题以获取说明

,给出:

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 
} 

当调用的类型平板状,例如Int,或String,或List[T],Scala编译器可以创建清单一类传递作为隐含参数到平板状。


25

清单旨在验证泛型类型,这些泛型类型被擦除后可以在JVM上运行(不支持泛型)。但是,它们存在一些严重的问题:它们过于简单,无法完全支持Scala的类型系统。因此,它们在Scala 2.10中已弃用TypeTag并由s 取代(本质上,这是Scala编译器本身用来表示类型的东西,因此完全支持Scala类型)。有关差异的更多详细信息,请参见:

换一种说法

您什么时候需要它?

在2013-01-04之前,Scala 2.10发行时


它尚未被弃用(但将会被弃用),因为Scala反射在2.10中仍处于实验阶段。
Keros

在2013年1月4日之前,或者您使用的是依赖它的API。
David Moles 2013年

1

让我们也CHCK出manifestscala源(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]并且作为manifest函数是类型manifest[T],它将搜索特定对象manifest[String],并查找是否存在这样的隐式对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.