Scala: что такое тег типа и как его использовать?



все, что я знаю о типах, это то, что они каким-то образом заменили манифесты. Информация в Интернете скудна и не дает мне хорошего представления о предмете.



поэтому я был бы рад, если бы кто-то поделился ссылкой на некоторые полезные материалы по типам, включая примеры и популярные варианты использования. Подробные ответы и объяснения также приветствуются.

714   1  

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. Существуют три различные формы метки:

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

    Ничего не найдено.