12

TypeTag を手動で作成することに興味があります (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結果は

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

型がハードコーディングされているため、完全にコンパイラの魔法のようです。それにもかかわらず、これを手動で行う方法はありますか?

4

4 に答える 4

10

Get TypeTag[A] from Class[A]に基づく:

import scala.reflect.runtime.universe._

def typeToTypeTag[T](
  tpe: Type,
  mirror: reflect.api.Mirror[reflect.runtime.universe.type]
): TypeTag[T] = {
  TypeTag(mirror, new reflect.api.TypeCreator {
    def apply[U <: reflect.api.Universe with Singleton](m: reflect.api.Mirror[U]) = {
      assert(m eq mirror, s"TypeTag[$tpe] defined in $mirror cannot be migrated to $m.")
      tpe.asInstanceOf[U#Type]
    }
  })
}

たとえば、これを使用してTypeTag、別の の一部を取得できTypeTagます。

def inside[A, B](tag: TypeTag[(A, B)]): (TypeTag[A], TypeTag[B]) = {
  val tpes = tag.tpe.asInstanceOf[TypeRefApi].args
  val tagA = typeToTypeTag[A](tpes(0), tag.mirror)
  val tagB = typeToTypeTag[B](tpes(1), tag.mirror)
  return (tagA, tagB)
}

これは Scala 2.10.2 で動作します:

scala> inside(typeTag[(Int, Double)])
res0: (reflect.runtime.universe.TypeTag[Int], reflect.runtime.universe.TypeTag[Double]) = (TypeTag[Int],TypeTag[Double])

Mirror複数の を持たない限り、特定のものに結び付けられるという制限は問題にならない可能性がありますClassLoader

于 2014-09-05T17:26:03.997 に答える
10

M3 では、タイプ タグを非常に簡単な方法で作成できますTypeTag[Int](TypeRef(<scala package>, <symbol of scala.Int>, Nil))。これは基本的に、型タグが作成されると、特定のクラスローダー (つまり、上記の例で scala.Int のシンボルをロードするために使用されたもの) に永久にバインドされることを意味していました。

当時は問題ありませんでした。なぜなら、すべてのクラスローダに対応するフリーサイズのミラーを持つことができると考えていたからです。implicitly[TypeTag[T]]書くだけで、コンパイラはそのグローバル ミラーを使用して、要求された Type をインスタンス化するため、これは便利でした。

残念ながら、後でフィードバックに基づいて、これはうまくいかず、それぞれが独自のクラスローダーを持つ複数のミラーが必要であることに気付きました。

そして、型タグは柔軟である必要があることが明らかになりました。なぜなら、一度書くとimplicitly[TypeTag[T]]、どのクラスローダを使用したいかについてコンパイラが十分な情報を持っていないからです。基本的に 2 つの選択肢がありました。1) 型タグをミラーにパス依存にする (型タグがコンパイラから要求されるたびに明示的にミラーを提供するように強制されるようにするため)、2) 型タグを型ファクトリに変換し、対応する任意のミラーで自分自身をインスタンス化する。簡単に言えば、最初のオプションはうまくいかなかったので、私たちはここにいます。

そのため、現在、型タグは、https://github.com/scala/scala/blob/master/src/library/scala/reflect/base/TypeTags.scala#L143で概説されているように、かなり迂回して作成する必要があります。コンパニオンTypeTagで定義されたファクトリ メソッドを呼び出し、次の 2 つの引数を指定します。1) 型タグがインスタンス化される既定のミラー、2) 任意のミラーで型タグをインスタンス化できる型ファクトリ。これは基本的に、上記で添付した印刷物でコンパイラが行ったことです。

なぜ私はこのラウンドアバウトと呼んだのですか?型ファクトリを提供するには、クラスを手動でサブクラス化する必要があるためですscala.reflect.base.TypeFactory。これは、関数と依存型メソッドの境界における Scala 型システムの残念な制限です。

于 2012-07-16T12:06:59.247 に答える
6

関数定義

def tt[A : TypeTag](a: A) = typeTag[A]

は単なる別の書き方です

def tt(a: A)(implicit tag: TypeTag[A]) = tag

これは、タグのインスタンスがコンパイラによって暗黙的に作成されていることを意味します。この背後にある理由は、Scala コンパイラが JVM の型消去の問題を、そうでなければ消去される型情報をハードコーディングすることで解決するためです。

型消去の問題に慣れていない場合は、JVM が型パラメーター情報を保存しないということです。たとえば、型Seq[Set[Int]]は JVM によって単純に認識Seq[_]され、欠落しているパラメーターを見つける方法はありません。リフレクションで情報を入力します。

于 2012-07-15T21:34:02.853 に答える