scalascala-reflect

How to create a TypeTag manually?


I'm interested in creating a TypeTag manually (since 2.10M5):

object X {
  import reflect.runtime.universe._
  def tt[A : TypeTag](a: A) = typeTag[A] // how to do this manually?
  val t = tt(List("")(_))
}

scalac -Xprint:typer <file>.scala results in

package <empty> {
  object X extends scala.AnyRef {
    def <init>(): X.type = {
      X.super.<init>();
      ()
    };
    import scala.reflect.runtime.`package`.universe._;
    def tt[A >: Nothing <: Any](a: A)(implicit evidence$1: reflect.runtime.universe.TypeTag[A]): reflect.runtime.universe.TypeTag[A] = scala.reflect.runtime.`package`.universe.typeTag[A](evidence$1);
    private[this] val t: reflect.runtime.universe.TypeTag[Int => String] = X.this.tt[Int => String](((x$1: Int) => immutable.this.List.apply[String]("").apply(x$1)))({
      val $u: reflect.runtime.universe.type = scala.this.reflect.runtime.`package`.universe;
      val $m: $u.Mirror = scala.this.reflect.runtime.`package`.universe.runtimeMirror(this.getClass().getClassLoader());
      $u.TypeTag.apply[Int => String]($m, {
        final class $typecreator1 extends TypeCreator {
          def <init>(): $typecreator1 = {
            $typecreator1.super.<init>();
            ()
          };
          def apply[U >: Nothing <: scala.reflect.base.Universe with Singleton]($m$untyped: scala.reflect.base.MirrorOf[U]): U#Type = {
            val $u: U = $m$untyped.universe;
            val $m: $u.Mirror = $m$untyped.asInstanceOf[$u.Mirror];
            $u.TypeRef.apply($u.ThisType.apply($m.staticModule("scala").asModuleSymbol.moduleClass), $m.staticClass("scala.Function1"), scala.collection.immutable.List.apply[$u.Type]($m.staticClass("scala.Int").asTypeSymbol.asTypeConstructor, $m.staticClass("java.lang.String").asTypeSymbol.asTypeConstructor))
          }
        };
        new $typecreator1()
      })
    });
    <stable> <accessor> def t: reflect.runtime.universe.TypeTag[Int => String] = X.this.t
  }
}

It seems to be completely compiler magic because the types are hardcoded. Nevertheless is there a way to do this manually?


Solution

  • In M3 you could create a type tag in a very simple way, e.g.: TypeTag[Int](TypeRef(<scala package>, <symbol of scala.Int>, Nil)). It basically meant that once a type tag is created, it is forever bound to a certain classloader (namely the one that was used to load a symbol of scala.Int in the example above).

    Back then it was fine, because we considered that we could have a one-size-fits-all mirror that accomodates all classloaders. That was convenient, because you could just write implicitly[TypeTag[T]] and the compiler would use that global mirror to instantiate a Type you requested.

    Unfortunately later, based on feedback, we realized that this isn't going to work, and that we need multiple mirrors - each having its own classloader.

    And then it became apparent that type tags need to be flexible, because once you write that implicitly[TypeTag[T]] the compiler doesn't have enough information what classloader you want to use. Basically there were two alternatives: 1) make type tags path-dependent on mirrors (so that one would be forced to explicitly provide a mirror every time a type tag is requested from the compiler), 2) transform type tags into type factories, capable of instantiating themselves in any mirror. Long story short, the first option didn't work, so we are where we are.

    So currently type tags need to be created in quite a roundabout way, as outlined in https://github.com/scala/scala/blob/master/src/library/scala/reflect/base/TypeTags.scala#L143. You call a factory method defined in the companion TypeTag and provide two arguments: 1) a default mirror, where the type tag will be instantiated, 2) a type factory that can instantiate a type tag in arbitrary mirror. This is basically what the compiler did in the printout you've attached above.

    Why I called this roundabout? Because to provide a type factory you need to manually subclass the scala.reflect.base.TypeFactory class. That's an unfortunate limitation of Scala type system on the border between function and dependently-typed methods.