Scala: что такое тег типа и как его использовать?
все, что я знаю о типах, это то, что они каким-то образом заменили манифесты. Информация в Интернете скудна и не дает мне хорошего представления о предмете.
поэтому я был бы рад, если бы кто-то поделился ссылкой на некоторые полезные материалы по типам, включая примеры и популярные варианты использования. Подробные ответы и объяснения также приветствуются.
1 ответ:
A
TypeTagрешает проблему, что типы Scala стираются во время выполнения (тип стирания). Если мы хотим сделать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. Но у них есть проблема, не способная представлять много полезных типов, таких как path-dependent-types:
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] = [email protected]#Foo$Bar scala> val ev2 = m(f2)(b2) warning: there were 2 deprecation warnings; re-run with -deprecation for details ev2: Manifest[f2.Bar] = [email protected]#Foo$Bar scala> ev1 == ev2 // they should be different, thus the result is wrong res28: Boolean = trueтаким образом, они заменяются TypeTags, которые оба гораздо проще в использовании и хорошо интегрирован в новый 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когда вызывается метод, ожидающий такогоTypeTag. Существуют три различные формы метки:
- скала.отражать.Classstag
- скала.отражать.прикладной программный интерфейс.TypeTags#TypeTag
- скала.отражать.прикладной программный интерфейс.TypeTags#WeakTypeTag
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: 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как видно, метод
tpeнаTypeTagрезультаты в полнойType, что то же самое мы получаем, когдаtypeOfназывается. Конечно, можно использовать оба,ClassTagиTypeTag:scala> def m[A : ClassTag : TypeTag] = (classTag[A], typeTag[A]) m: [A](implicit evidence: scala.reflect.ClassTag[A],↩ implicit evidence: 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: 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] ^более подробно об различия между
TypeTagиWeakTypeTagсм. Этот вопрос: макросы Scala: "не удается создать тег типа T с неразрешенными параметрами типа"официальный сайт документации Scala также содержит руководство для размышления.
Comments