How does Scala's (2.8) Manifest work?

前端 未结 2 1121
执念已碎
执念已碎 2020-12-02 07:20

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 ar

相关标签:
2条回答
  • 2020-12-02 07:57

    The "context bound" T ... : Manifest is syntactic sugar for an implicit argument: (implicit man: Manifest[T]). Thus at the point of instantiation of the type constructor specified by class Image, the compiler finds / supplies the Manifest for the actual type used for the type parameter T and that value "sticks with" the resulting class instance throughout its existence and "anchors" each particular instance of Image[Something] to the Manifest for its T.

    0 讨论(0)
  • 2020-12-02 07:59

    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();
            ()
          }
        }
      }
    }
    
    0 讨论(0)
提交回复
热议问题