Scala:TypeTagとは何ですか?どのように使用しますか?


361

TypeTagについて私が知っているのは、マニフェストを何らかの方法で置き換えたことだけです。インターネット上の情報は乏しく、主題についての良い感覚を私に提供しません。

ですから、例や人気のあるユースケースを含め、TypeTagsに関するいくつかの有用な資料へのリンクを誰かが共有してくれれば幸いです。詳細な回答や説明も大歓迎です。


1
Scalaドキュメンテーションの次の記事では、タイプタグの内容と理由、およびコードでのタグの使用方法について説明しています。docs.scala
lang.org

回答:


563

Aは、TypeTagScalaの型は実行時(型消去)で消去されてしまうという問題を解決します。したいなら

class Foo
class Bar extends Foo

def meth[A](xs: List[A]) = xs match {
  case _: List[String] => "list of strings"
  case _: List[Foo] => "list of foos"
}

警告が表示されます:

<console>:23: warning: non-variable type argument String in type pattern List[String]↩
is unchecked since it is eliminated by erasure
         case _: List[String] => "list of strings"
                 ^
<console>:24: warning: non-variable type argument Foo in type pattern List[Foo]↩
is unchecked since it is eliminated by erasure
         case _: List[Foo] => "list of foos"
                 ^

この問題を解決するために、マニフェストがScalaに導入されました。しかし、パスに依存するタイプなど、多くの有用なタイプを表現できないという問題があります。

scala> class Foo{class Bar}
defined class Foo

scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev
warning: there were 2 deprecation warnings; re-run with -deprecation for details
m: (f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar])Manifest[f.Bar]

scala> val f1 = new Foo;val b1 = new f1.Bar
f1: Foo = Foo@681e731c
b1: f1.Bar = Foo$Bar@271768ab

scala> val f2 = new Foo;val b2 = new f2.Bar
f2: Foo = Foo@3e50039c
b2: f2.Bar = Foo$Bar@771d16b9

scala> val ev1 = m(f1)(b1)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev1: Manifest[f1.Bar] = Foo@681e731c.type#Foo$Bar

scala> val ev2 = m(f2)(b2)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev2: Manifest[f2.Bar] = Foo@3e50039c.type#Foo$Bar

scala> ev1 == ev2 // they should be different, thus the result is wrong
res28: Boolean = true

したがって、これらはTypeTagsに置き換えられます。TypeTagsは使用がはるかに簡単で、新しいReflection APIによく統合されています。それらを使用して、パス依存型に関する上記の問題をエレガントに解決できます。

scala> def m(f: Foo)(b: f.Bar)(implicit ev: TypeTag[f.Bar]) = ev
m: (f: Foo)(b: f.Bar)(implicit ev: reflect.runtime.universe.TypeTag[f.Bar])↩
reflect.runtime.universe.TypeTag[f.Bar]

scala> val ev1 = m(f1)(b1)
ev1: reflect.runtime.universe.TypeTag[f1.Bar] = TypeTag[f1.Bar]

scala> val ev2 = m(f2)(b2)
ev2: reflect.runtime.universe.TypeTag[f2.Bar] = TypeTag[f2.Bar]

scala> ev1 == ev2 // the result is correct, the type tags are different
res30: Boolean = false

scala> ev1.tpe =:= ev2.tpe // this result is correct, too
res31: Boolean = false

また、タイプパラメータをチェックするのにも簡単に使用できます。

import scala.reflect.runtime.universe._

def meth[A : TypeTag](xs: List[A]) = typeOf[A] match {
  case t if t =:= typeOf[String] => "list of strings"
  case t if t <:< typeOf[Foo] => "list of foos"
}

scala> meth(List("string"))
res67: String = list of strings

scala> meth(List(new Bar))
res68: String = list of foos

この時点で、=:=(タイプの等価性)と<:<(サブタイプの関係)を使用して等価性チェックを行うことを理解することは非常に重要です。あなたが何をしているのか絶対にわかっているのでない限り、絶対に==or !=を使わないでください:

scala> typeOf[List[java.lang.String]] =:= typeOf[List[Predef.String]]
res71: Boolean = true

scala> typeOf[List[java.lang.String]] == typeOf[List[Predef.String]]
res72: Boolean = false

後者は、構造の等価性をチェックします。これは、(例のように)接頭辞などのことを気にしないため、実行する必要のあることではないことがよくあります。

A TypeTagは完全にコンパイラーによって生成されます。つまり、そのTypeTagようなを期待するメソッドを呼び出すと、コンパイラーはを作成して埋めTypeTagます。タグには3つの異なる形式があります。

ClassTag代替ClassManifest一方TypeTagのため、多かれ少なかれ代替品ですManifest

前者は一般的な配列を完全に処理することを可能にします:

scala> import scala.reflect._
import scala.reflect._

scala> def createArr[A](seq: A*) = Array[A](seq: _*)
<console>:22: error: No ClassTag available for A
       def createArr[A](seq: A*) = Array[A](seq: _*)
                                           ^

scala> def createArr[A : ClassTag](seq: A*) = Array[A](seq: _*)
createArr: [A](seq: A*)(implicit evidence$1: scala.reflect.ClassTag[A])Array[A]

scala> createArr(1,2,3)
res78: Array[Int] = Array(1, 2, 3)

scala> createArr("a","b","c")
res79: Array[String] = Array(a, b, c)

ClassTag 実行時にタイプを作成するために必要な情報のみが提供されます(タイプは消去されます)。

scala> classTag[Int]
res99: scala.reflect.ClassTag[Int] = ClassTag[int]

scala> classTag[Int].runtimeClass
res100: Class[_] = int

scala> classTag[Int].newArray(3)
res101: Array[Int] = Array(0, 0, 0)

scala> classTag[List[Int]]
res104: scala.reflect.ClassTag[List[Int]] =ClassTag[class scala.collection.immutable.List]

上記のように、型の消去は気にしません。したがって、「完全な」型TypeTagを使用したい場合は、次のように使用します。

scala> typeTag[List[Int]]
res105: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]

scala> typeTag[List[Int]].tpe
res107: reflect.runtime.universe.Type = scala.List[Int]

scala> typeOf[List[Int]]
res108: reflect.runtime.universe.Type = scala.List[Int]

scala> res107 =:= res108
res109: Boolean = true

ご覧のとおり、メソッドtpeTypeTag結果はfull Typeになります。これは、typeOf呼び出されたときに取得されるものと同じです。もちろん、両方を使用することが可能である、ClassTagTypeTag

scala> def m[A : ClassTag : TypeTag] = (classTag[A], typeTag[A])
m: [A](implicit evidence$1: scala.reflect.ClassTag[A],implicit evidence$2: reflect.runtime.universe.TypeTag[A])(scala.reflect.ClassTag[A], reflect.runtime.universe.TypeTag[A])

scala> m[List[Int]]
res36: (scala.reflect.ClassTag[List[Int]],↩
        reflect.runtime.universe.TypeTag[List[Int]]) =(scala.collection.immutable.List,TypeTag[scala.List[Int]])

残りの質問は今の意味はWeakTypeTag何ですか?つまり、TypeTag具体的な型を表します(つまり、完全にインスタンス化された型のみを許可します)WeakTypeTag。ほとんどの場合、どちらが何であるか(つまりTypeTag、使用する必要があるか)は気にしませんが、たとえば、マクロが使用されている場合に、それらが必要なジェネリック型で機能する必要があります。

object Macro {
  import language.experimental.macros
  import scala.reflect.macros.Context

  def anymacro[A](expr: A): String = macro __anymacro[A]

  def __anymacro[A : c.WeakTypeTag](c: Context)(expr: c.Expr[A]): c.Expr[A] = {
    // to get a Type for A the c.WeakTypeTag context bound must be added
    val aType = implicitly[c.WeakTypeTag[A]].tpe
    ???
  }
}

エラーで置き換えWeakTypeTagられTypeTagた場合はスローされます:

<console>:17: error: macro implementation has wrong shape:
 required: (c: scala.reflect.macros.Context)(expr: c.Expr[A]): c.Expr[String]
 found   : (c: scala.reflect.macros.Context)(expr: c.Expr[A])(implicit evidence$1: c.TypeTag[A]): c.Expr[A]
macro implementations cannot have implicit parameters other than WeakTypeTag evidences
             def anymacro[A](expr: A): String = macro __anymacro[A]
                                                      ^

違いに関する詳細な説明については、この質問TypeTagWeakTypeTag参照してください。Scalaマクロ:「未解決の型パラメーターを持つ型TからTypeTagを作成できません」

Scalaの公式ドキュメントサイトには、Reflectionのガイドも含まれています


19
ご回答有難うございます!いくつかのコメント:1)==型は構造の等価性を表し、参照の等価性を表すものではありません。=:=アカウントの種類の等価性(例えば異なるミラーから来るプレフィックスの等価としても、非自明なもの)、2)の両方を考慮に入れるTypeTagAbsTypeTag、ミラーに基づいています。違いは、TypeTag完全にインスタンス化された型のみを許可することです(つまり、型パラメーターまたは参照抽象型メンバーを参照しません)。3)詳細な説明はこちら:stackoverflow.com/questions/12093752
Eugene Burmako

10
4)マニフェストには、多くの有用なタイプを表現できないという問題があります。基本的に、それらは、型参照(などの単純型Intおよびなどの一般型List[Int])のみを表現でき、たとえば、絞り込み、パス依存型、存在、注釈付き型などのScala型を除外します。コンパイラposessesなど、1種類の別のサブタイプかどうかを見つける、タイプの線形化を計算し、言っていることを、彼らは膨大な知識を使用することはできませんので、またマニフェストは、上のボルトです
ユージンBurmako

9
5)対比タイプのタグが「よりよく統合」されていないため、新しいリフレクションAPIと統合されているだけです(何も統合されていないマニフェストとは異なります)。これにより、コンパイラーの特定の側面へのタイプタグアクセスが提供されます。たとえば、Types.scala(タイプが一緒に機能するためにサポートされSymbols.scalaている方法を知っている7klocのコード)、(シンボルテーブルがどのように機能しているかを知っている3klocのコード)など
Eugene Burmako

9
6)ClassTagはの完全な代替品ですClassManifestが、TypeTagは多かれ少なかれの代替品ですManifest。多かれ少なかれ、1)タイプタグは消去を行わない、2)マニフェストは大きなハックであり、タイプタグを使用してその動作をエミュレートすることをあきらめました。#1は、消去とタイプの両方が必要な場合にClassTagとTypeTagの両方のコンテキスト境界を使用することで修正できます。通常は、すべてのハックを破棄して本格的なリフレクションAPIを使用できるようになるため、#2は気にしません。代わりに。
Eugene Burmako 2012

11
私は、Scalaコンパイラーが廃止予定の機能を削除して、利用可能な機能のセットをより直交化できるようになることを期待しています。これが新しいマクロのサポートが好きな理由です。新しいマクロのサポートは、言語をクリーンアップして、基本言語の一部ではない独立したライブラリーの一部の機能を分離する可能性を提供するためです。
Alexandru Nedelcu 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.