Scala(2.8)Manifest如何工作? [英] How does Scala's (2.8) Manifest work?

查看:95
本文介绍了Scala(2.8)Manifest如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些Scala代码,使泛型的使用相当繁重,我从文档中收集到的参数化约束中使用清单可以帮助我解决类型擦除问题(例如,我想实例化一个新的对象泛型类型)。只是,我想更多地了解它是如何工作的。它几乎感觉就像某种散列表,它为每个调用网站获得一个条目......任何人都可以在这里详细说明吗?

  class Image [T <:Pixel [T]:Manifest](fun()=> T,size:Array [Int],data:Array [T]){
def this(fun:()=> (size,size * size(1));
}
}

这是我在网站上找到的任何文档都没有涉及的东西,在Google上,我大部分的帖子都是老式的,语法不同,因为2.8似乎有很多变化,我不确定这些信息是否准确。

解决方案

这是一段时间,因为我挖掘了斯卡拉的源代码,试图回答同样的问题......但我记得简短的回答 -



Manifest是一个作弊代码,允许编译器绕过类型擦除(它不在runti中使用它会导致在编译时为匹配清单的可能输入类型生成多个代码路径。



清单是隐式解析的,但如果有任何歧义在编译时关于Manifest类型是什么,编译器将停止。



使用 Manifest 的副本有几件事情可用。您通常需要的主要内容是通过擦除擦除的 java.lang.Class

  class BoundedManifest [T<:Any:Manifest](value:T){
val m = manifest [T]
m.erasure.toString match {
caseclass java.lang.String=> println(String)
casedouble| int=> println(Numeric value。)
case x =>> println(WTF是'%s'?. format(x))
}
}

class ImplicitManifest [T< ;: Any](value:T) (隐式m:Manifest [T]){
m.erasure.toString match {
caseclass java.lang.String=> println(String)
casedouble| int=> println(Numeric value。)
case x =>> println(WTF是'%s'?。format(x))
}
}

新的BoundedManifest(Foo Bar!)
/ / String
new BoundedManifest(5)
//数字值。
new BoundedManifest(5.2)
//数值。
new BoundedManifest(BigDecimal(8.62234525))
// WTF是'class scala.math.BigDecimal'?
新ImplicitManifest(Foo Bar!)
//字符串
新ImplicitManifest(5)
//数字值。
新ImplicitManifest(5.2)
//数字值。
新ImplicitManifest(BigDecimal(8.62234525))
// WTF是'class scala.math.BigDecimal'?

这是一个相当不吉利的例子,但显示了正在发生的事情。



[T ...:Manifest] 我在Scala 2.8上运行该输出以及FWIW。边界在Scala 2.8中是新的......你以前必须隐式地抓取清单,如 ImplicitManifest 所示。你实际上并没有获得Manifest的副本。但您可以通过说明 val m = manifest [T] ... manifest [_] 定义在 Predef 上,并且可以在限制区块内找到适当的清单类型。



另外两个主要项目您可以从 Manifest 获得<<:< 和>:> 它测试一个清单的子类型/超类型与另一个清单。如果我正确记得这些是非常天真的执行方式,并不总是匹配,但我有一堆生产代码使用它们来测试一些可能的擦除输入。检查另一个清单的一个简单示例:

pre $ class BoundedManifestCheck [T<:Any:Manifest](value:T){ (m <: println(AnyVal(primitive))
} else if(
val m = manifest [T]
if m <:< manifest [AnyRef]){
println(AnyRef)
} else {
println(不确定基本类型的清单'%s'是什么。 .format(m.erasure))
}
}


new BoundedManifestCheck(Foo Bar!)
// AnyRef
new BoundedManifestCheck(5)
// AnyVal(原始)
new BoundedManifestCheck(5.2)
// AnyVal(原始)
new BoundedManifestCheck(BigDecimal(8.62234525))
// AnyRef

Jorge Ortiz在这篇文章中有一篇很棒的博客文章(虽然很老):< a href =http://www.scala-blogs.org/2008/10/manifests-reified-types.html =noreferrer> http://www.scala-blogs.org/2008/10/ manifests-reified-types.html



编辑

您可以通过要求它输出擦除编译器阶段的结果来真正了解Scala在做什么。



正在运行,在我上面的 scala -Xprint:erasure test.scala 上面的例子中产生以下结果:

  final class Main扩展java.lang.Object与ScalaObject {
def this():object Main = {
Main.super.this();
()
};
def main(argv:Array [java.lang.String]):Unit = {
val args:Array [java.lang.String] = argv;
{
final class $ anon extends java.lang.Object {
def this():anonymous class $ anon = {
$ anon.super.this();
()
};
class BoundedManifestCheck扩展了带有ScalaObject的java.lang.Object {
< paramaccessor> private [this] val value:java.lang.Object = _;
隐式< paramaccessor> private [this] val证据$ 1:scala.reflect.Manifest = _;
def this($ outer:anonymous class $ anon,value:java.lang.Object,evidence $ 1:scala.reflect.Manifest):BoundedManifestCheck = {
BoundedManifestCheck.super.this();
()
};
private [this] val m:scala.reflect.Manifest = scala.this.Predef.manifest(BoundedManifestCheck.this.evidence $ 1);
< stable> <存取> def m():scala.reflect.Manifest = BoundedManifestCheck.this.m;
if(BoundedManifestCheck.this.m()。<:<(scala.this.Predef.manifest(reflect.this.Manifest.AnyVal())))
scala.this.Predef。 println(AnyVal(primitive))
else
if(BoundedManifestCheck.this.m()。<:<(scala.this.Predef.manifest(reflect.this.Manifest.Object ))))
scala.this.Predef.println(AnyRef)
else
scala.this.Predef.println(scala.this.Predef.augmentString(不确定是什么基本类型的清单%s是。)。format(scala.this.Predef.genericWrapArray(Array [java.lang.Object] {BoundedManifestCheck.this.m()。erasure()})));
保护<合成> < paramaccessor> val $ outer:匿名类$ anon = _;
<合成> <稳定的GT; def Main $$ anon $ BoundedManifestCheck $$$ outer():anonymous class $ anon = BoundedManifestCheck.this。$ outer
};
new BoundedManifestCheck($ anon.this,Foo Bar!,reflect.this.Manifest.classType(classOf [java.lang.String]));
new BoundedManifestCheck($ anon.this,scala.Int.box(5),reflect.this.Manifest.Int());
new BoundedManifestCheck($ anon.this,scala.Double.box(5.2),reflect.this.Manifest.Double());
new BoundedManifestCheck($ anon.this,scala.package.BigDecimal()。apply(8.62234525),reflect.this.Manifest.classType(classOf [scala.math.BigDecimal]))
} ;
{
新的匿名类$ anon();
()
}
}
}
}


I have some Scala code that makes fairly heavy use of generics, and I have gleaned from the docs that using a manifest in the parametrization constraints can help me work around the type erasure issues (e.g. I want to instantiate a new object of the generic type). Only, I'd like to understand more about how this works. It almost feels like some sort of hashmap that's getting an entry for every invocation site... Can anyone here elaborate?

class Image[T <: Pixel[T] : Manifest](fun() => T, size: Array[Int], data: Array[T]) {
    def this(fun: () => T, size: Array[T]) {
        this(fun, size, new Array[T](size(0) * size(1));
    }
}

This is something that doesn't seem to be covered in any of the documentation that I found on the site, and on Google I mostly get older posts that have very different syntax, and since 2.8 seems to have a lot of things changed, I'm not sure those are still accurate.

解决方案

It's been awhile since I dug through the source code for Scala in a quest to answer the same question... but the short answer, as I recall -

Manifest is a cheat code to allow the COMPILER to get around Type erasure (it isn't being used at runtime). It causes multiple code paths to be generated at compile time for the possible input types matching the manifest.

The Manifest is resolved implicitly, but if there is any ambiguity at compile time about what the Manifest type is, the compiler WILL stop.

With a copy of a Manifest you have a few things available. The main things you typically want is either the java.lang.Class that was erased via erasure:

class BoundedManifest[T <: Any : Manifest](value: T) {
  val m = manifest[T]
  m.erasure.toString match {
    case "class java.lang.String" => println("String")
    case "double" | "int"  => println("Numeric value.")
    case x => println("WTF is a '%s'?".format(x))
    }
}

class ImplicitManifest[T <: Any](value: T)(implicit m: Manifest[T]) {
  m.erasure.toString match {
    case "class java.lang.String" => println("String")
    case "double" | "int" => println("Numeric value.")
    case x => println("WTF is a '%s'?".format(x))
  }
}

new BoundedManifest("Foo Bar!")
// String 
new BoundedManifest(5)
// Numeric value.
new BoundedManifest(5.2)
// Numeric value.
new BoundedManifest(BigDecimal("8.62234525"))
// WTF is a 'class scala.math.BigDecimal'?
new ImplicitManifest("Foo Bar!")
// String 
new ImplicitManifest(5)
// Numeric value.
new ImplicitManifest(5.2)
// Numeric value.
new ImplicitManifest(BigDecimal("8.62234525"))
// WTF is a 'class scala.math.BigDecimal'?

This is a rather wonky example but shows what is going on. I ran that for the output as well FWIW on Scala 2.8.

The [T ... : Manifest] boundary is new in Scala 2.8... you used to have to grab the manifest implicitly as shown in ImplicitManifest. You don't actually GET a copy of the Manifest. But you can fetch one inside your code by saying val m = manifest[T] ... manifest[_] is defined on Predef and demonstrably will find the proper manifest type inside a boundaried block.

The other two major items you get from a Manifest is <:< and >:> which test subtype/supertype of one manifest versus another. If I recall correctly these are VERY naive implementation wise and don't always match but I have a bunch of production code using them to test against a few possible erased inputs. A simple example of checking against another manifest:

class BoundedManifestCheck[T <: Any : Manifest](value: T) {
  val m = manifest[T]
  if (m <:< manifest[AnyVal]) {
    println("AnyVal (primitive)")
  } else if (m <:< manifest[AnyRef]) {
    println("AnyRef")
  } else {
    println("Not sure what the base type of manifest '%s' is.".format(m.erasure))
  }
}


new BoundedManifestCheck("Foo Bar!")
// AnyRef
new BoundedManifestCheck(5)
// AnyVal (primitive)
new BoundedManifestCheck(5.2)    
// AnyVal (primitive)
new BoundedManifestCheck(BigDecimal("8.62234525"))
// AnyRef

Jorge Ortiz has a great blog post (albeit old) on this: http://www.scala-blogs.org/2008/10/manifests-reified-types.html

EDIT:

You can actually see what Scala is doing by asking it to print out the results of the erasure compiler phase.

Running, on my last example above scala -Xprint:erasure test.scala produces the following result:

final class Main extends java.lang.Object with ScalaObject {
  def this(): object Main = {
    Main.super.this();
    ()
  };
  def main(argv: Array[java.lang.String]): Unit = {
    val args: Array[java.lang.String] = argv;
    {
      final class $anon extends java.lang.Object {
        def this(): anonymous class $anon = {
          $anon.super.this();
          ()
        };
        class BoundedManifestCheck extends java.lang.Object with ScalaObject {
          <paramaccessor> private[this] val value: java.lang.Object = _;
          implicit <paramaccessor> private[this] val evidence$1: scala.reflect.Manifest = _;
          def this($outer: anonymous class $anon, value: java.lang.Object, evidence$1: scala.reflect.Manifest): BoundedManifestCheck = {
            BoundedManifestCheck.super.this();
            ()
          };
          private[this] val m: scala.reflect.Manifest = scala.this.Predef.manifest(BoundedManifestCheck.this.evidence$1);
          <stable> <accessor> def m(): scala.reflect.Manifest = BoundedManifestCheck.this.m;
          if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.AnyVal())))
            scala.this.Predef.println("AnyVal (primitive)")
          else
            if (BoundedManifestCheck.this.m().<:<(scala.this.Predef.manifest(reflect.this.Manifest.Object())))
              scala.this.Predef.println("AnyRef")
            else
              scala.this.Predef.println(scala.this.Predef.augmentString("Not sure what the base type of manifest '%s' is.").format(scala.this.Predef.genericWrapArray(Array[java.lang.Object]{BoundedManifestCheck.this.m().erasure()})));
          protected <synthetic> <paramaccessor> val $outer: anonymous class $anon = _;
          <synthetic> <stable> def Main$$anon$BoundedManifestCheck$$$outer(): anonymous class $anon = BoundedManifestCheck.this.$outer
        };
        new BoundedManifestCheck($anon.this, "Foo Bar!", reflect.this.Manifest.classType(classOf[java.lang.String]));
        new BoundedManifestCheck($anon.this, scala.Int.box(5), reflect.this.Manifest.Int());
        new BoundedManifestCheck($anon.this, scala.Double.box(5.2), reflect.this.Manifest.Double());
        new BoundedManifestCheck($anon.this, scala.package.BigDecimal().apply("8.62234525"), reflect.this.Manifest.classType(classOf[scala.math.BigDecimal]))
      };
      {
        new anonymous class $anon();
        ()
      }
    }
  }
}

这篇关于Scala(2.8)Manifest如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆