Scala: ¿Qué es una etiqueta tipográfica y cómo la uso?


Todo lo que sé sobre las etiquetas tipográficas es que de alguna manera reemplazaron los manifiestos. La información en Internet es escasa y no me proporciona un buen sentido del tema.

Así que estaría feliz si alguien compartiera un enlace a algunos materiales útiles sobre etiquetas tipográficas, incluidos ejemplos y casos de uso populares. Las respuestas y explicaciones detalladas también son bienvenidas.

Author: Sergey Weiss, 2012-08-31

1 answers

A TypeTag resuelve el problema de que los tipos de Scala se borran en tiempo de ejecución (type erasure). Si queremos hacer

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

Recibiremos advertencias: {[41]]}

<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"
                 ^

Para resolver este problema Se introdujeron manifiestos a Scala. Pero tienen el problema de no poder representar muchos tipos útiles, como los tipos dependientes de la ruta:

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

Por lo tanto, son reemplazados por TypeTags, que son mucho más simples de usar y están bien integrados en la nueva API de reflexión. Con ellos podemos resolver el problema anterior sobre los tipos dependientes del camino elegantemente:

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

También son fáciles de usar para verificar los parámetros de tipo:

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

En este punto, es extremadamente importante entender el uso de =:= (igualdad de tipo) y <:< (relación de subtipo) para verificaciones de igualdad. Nunca use == o !=, a menos que sepa absolutamente lo que hace:

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

Este último comprueba la igualdad estructural, que a menudo no es lo que se debe hacer porque no lo hace preocúpate por cosas como los prefijos (como en el ejemplo).

Un TypeTag está completamente generado por el compilador, lo que significa que el compilador crea y rellena un TypeTag cuando se llama a un método esperando tal TypeTag. Existen tres formas diferentes de etiquetas:

ClassTag sustitutos ClassManifest considerando TypeTag es más o menos el reemplazo de Manifest.

El primero permite trabajar completamente con matrices genéricas:

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 proporciona solo la información necesaria para crear tipos en tiempo de ejecución (que se borran):

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]

Como se puede ver arriba, no les importa el borrado de tipos, por lo tanto, si uno quiere tipos "completos" TypeTag deben usarse:

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

Como se puede ver, el método tpe de TypeTag resulta en un Type completo, que es el mismo que obtenemos cuando typeOf es called. Por supuesto, es posible usar ambos, ClassTag y 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]])

La pregunta restante ahora es ¿cuál es el sentido de WeakTypeTag? En resumen, TypeTag representa un tipo concreto (esto significa que solo permite tipos completamente instanciados) mientras que WeakTypeTag solo permite cualquier tipo. La mayoría de las veces a uno no le importa cuál es qué (lo que significa que TypeTag debe usarse), pero por ejemplo, cuando se usan macros que deben funcionar con tipos genéricos, se necesitan:

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

Si se sustituye WeakTypeTag con TypeTag se lanza un error:

<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]
                                                      ^

Para una explicación más detallada sobre las diferencias entre TypeTag y WeakTypeTag vea esta pregunta: Las Macros de Scala: "no se puede crear una etiqueta de tipo a partir de un tipo T con parámetros de tipo sin resolver"

El sitio oficial de documentación de Scala también contiene una guía para la Reflexión.

 510
Author: kiritsuku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:38