스칼라 : TypeTag 란 무엇이며 어떻게 사용합니까?


361

TypeTag에 대해 내가 아는 것은 매니페스트를 대체 한 것입니다. 인터넷에 관한 정보는 희소하며 그 주제에 대한 올바른 이해를 제공하지 않습니다.

따라서 누군가가 예제 및 인기있는 유스 케이스를 포함하여 TypeTag에 대한 유용한 자료에 대한 링크를 공유하면 기쁠 것입니다. 자세한 답변과 설명도 환영합니다.


1
스칼라 문서의 다음 기사에서는 유형 태그의 내용과 이유, 그리고 코드에서 태그를 사용하는 방법에 대해 설명합니다. docs.scala-lang.org/overviews/reflection/…
btiernay

답변:


563

A TypeTag는 런타임에 스칼라 유형이 지워지는 문제를 해결합니다 (유형 삭제). 우리가 원한다면

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 로 대체되어 사용하기가 훨씬 간단하고 새로운 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

이 시점에서 평등 검사에 =:=(유형 평등) 및 <:<(하위 유형 관계) 를 사용하는 것을 이해하는 것이 매우 중요 합니다. 사용하지 마십시오 ==또는 !=당신이 절대적으로 당신이 무엇을 알고하지 않는 :

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는 a를 기대하는 메소드를 호출 할 때 작성하여 작성 합니다 TypeTag. 태그에는 세 가지 형태가 있습니다.

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전체에 결과를 Type때 우리가 얻을 동일하다, typeOf라고합니다. 물론, 모두를 사용할 수있다, ClassTag그리고 TypeTag:

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

경우 일 명을 대체 WeakTypeTagTypeTag오류가 발생합니다 :

<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이 질문을 참조하십시오 "해결되지 않은 형식 매개 변수를 갖는 타입 T에서 TypeTag를 만들 수 없습니다": 스칼라 매크로

스칼라의 공식 문서 사이트에는 Reflection에 안내서 .


19
답변 주셔서 감사합니다! 일부 의견 : 1) ==유형의 경우 참조 평등이 아닌 구조적 평등을 나타냅니다. =:=계정 유형의 동등성 (예 : 다른 거울에서 오는 접두사의 동등성 심지어 비 명백한 것들), 2) 모두를 고려 TypeTag하고 AbsTypeTag거울을 기반으로합니다. 차이점은 TypeTag, 3) 상세한 설명은 여기에 (모든 종류의 매개 변수 또는 참조 추상 형식의 멤버없이 IE)에만 완전히 인스턴스화 허용 유형 : stackoverflow.com/questions/12093752
유진 Burmako

10
4) 매니페스트는 많은 유용한 유형을 표현할 수 없다는 문제가 있습니다. 기본적으로 그들은 단지 형 심판 (예 : 일반 유형 표현할 수 Int와 같은 일반적인 유형을 List[Int]예를 들어 개선, 경로 의존적 유형, existentials, 주석 유형과 같은 스칼라 유형을 떠나). 또한 매니페스트는 기본이므로 컴파일러는 형식의 선형화를 계산하고 한 유형이 다른 유형을 하위 유형으로 지정하는지 등을 알기 위해 컴파일러가 제시하는 방대한 지식을 사용할 수 없습니다.
Eugene 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에 신경 쓰지 않습니다. 대신에.
유진 버마 코

11
스칼라 컴파일러가 사용 가능한 기능을보다 직교 적으로 만들기 위해 더 이상 사용되지 않는 기능을 제거하기를 바랍니다. 이것이 새로운 매크로 지원을 좋아하는 이유입니다. 언어를 정리하고 기본 언어에 속하지 않는 독립 라이브러리에서 일부 기능을 분리 할 수 ​​있기 때문입니다.
Alexandru Nedelcu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.