Scala: TypeTag nedir ve nasıl kullanılır?


361

TypeTags hakkında bildiğim tek şey bir şekilde Manifestlerin yerini almaları. İnternetteki bilgiler azdır ve bana konuyla ilgili iyi bir fikir vermez.

Birisi, örnekler ve popüler kullanım örnekleri de dahil olmak üzere TypeTags üzerindeki bazı yararlı malzemelerin bağlantısını paylaşırsa mutlu olurum. Ayrıntılı cevaplar ve açıklamalar da kabul edilir.


1
Scala belgelerindeki aşağıdaki makale, tip etiketlerinin hem nedenini hem de nedenini ve bunları kodunuzda nasıl kullanacağınızı açıklamaktadır: docs.scala-lang.org/overviews/reflection/…
btiernay

Yanıtlar:


563

A TypeTag, Scala tiplerinin çalışma zamanında silinmesi sorununu çözer (tip silme). Eğer yapmak istiyorsak

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

uyarılar alacağız:

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

Bu sorunu çözmek için Manifestler Scala'ya tanıtıldı. Ancak, yola bağlı türler gibi birçok yararlı türü temsil edememe sorunu var:

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

Böylece, bunların her ikisi de kullanımı çok daha basit olan ve yeni Reflection API'sine iyi entegre olan TypeTags ile değiştirilir . Onlarla birlikte yola bağımlı türler hakkındaki sorunu zarif bir şekilde çözebiliriz:

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

Ayrıca tip parametrelerini kontrol etmek için kullanımı kolaydır:

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

Bu noktada, eşitlik kontrolleri için =:=(tip eşitliği) ve <:<(alt tip ilişkisi) kullanımını anlamak son derece önemlidir . İle asla temizlemeyin ==veya !=kesinlikle ne yaptığını bilmedikçe,:

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

İkincisi yapısal eşitliği kontrol eder, bu genellikle yapılması gereken şey değildir, çünkü önekler gibi şeyleri umursamaz (örnekteki gibi).

A TypeTagtamamen derleyici tarafından üretilir, yani derleyici TypeTagböyle bir a'yı bekleyen bir yöntemi çağırdığında bir derleyici oluşturur ve doldurur TypeTag. Üç farklı etiket biçimi vardır:

ClassTagyerine ClassManifestise TypeTagdaha fazla ya da daha az bir yedek Manifest.

Birincisi genel dizilerle tam olarak çalışmayı sağlar:

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 yalnızca çalışma zamanında türler oluşturmak için gereken bilgileri sağlar (bunlar tür silinir):

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]

Yukarıda görüldüğü gibi, tip silme umurunda değil, bu yüzden biri "tam" türleri TypeTagkullanılmalıdır:

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

Gördüğünüz gibi, tam tpeolarak TypeTagsonuç yöntemi denir Type, bu da ne zaman elde typeOfedilir. Tabii ki, her ikisini de kullanmak mümkündür, ClassTagve 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]])

Geriye kalan soru şimdi ne anlama geliyor WeakTypeTag? Kısacası, TypeTagsomut bir türü temsil eder (bu sadece tamamen somutlaştırılmış tiplere izin verdiği anlamına gelir), WeakTypeTagsadece herhangi bir türe izin verir. Çoğu zaman, hangisinin (hangi araçların TypeTagkullanılması gerektiğini) önemsemez , ancak örneğin, genel türlerle çalışması gereken makrolar kullanıldığında ihtiyaç duyulur:

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

Biri hata WeakTypeTagile değiştirilirse 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]
                                                      ^

Arasındaki farklar hakkında daha ayrıntılı bir açıklama için TypeTagve WeakTypeTag: Bu soruya bakın “çözülmemiş tür parametreleri olan bir tip T den TypeTag oluşturamazsınız”: Scala Makrolar

Scala'nın resmi dokümantasyon sitesi aynı zamanda Yansıma için bir rehber içerir .


19
Cevabınız için teşekkürler! Bazı yorumlar: 1) ==türler için referans eşitliği değil, yapısal eşitliği temsil eder. =:=hesap türü eşdeğerlik (örneğin farklı aynalar gelen ön eklerinin eşdeğerlik olarak bile olmayan belirgin olanları), 2) Hem önünde TypeTagve AbsTypeTagaynalar dayanmaktadır. Aradaki fark, TypeTagsadece tamamen başlatılmış türlere izin vermesidir (yani, herhangi bir tür parametresi veya soyut tür üyesi referansı olmadan), 3) Ayrıntılı bir açıklama burada: stackoverflow.com/questions/12093752
Eugene Burmako

10
4) Manifestlerin birçok yararlı türü temsil edememe sorunu vardır. Esasen, yalnızca ayrıntılandırmalar, yola bağlı türler, varoluşlar, açıklamalı türler gibi Scala türlerini dışarıda bırakarak , yalnızca tür ref'leri (örneğin düz türler Intve genel türler gibi List[Int]) ifade edebilirler . Ayrıca tezahürler bir cıvatadır, bu yüzden derleyicinin bir türün doğrusallaştırmasını hesaplamak, bir türün diğerini alt
türlere

9
5) Kontrast tipi etiketler "daha iyi entegre edilmez", sadece yeni yansıma API'siyle (hiçbir şeyle entegre olmayan manifestlerin aksine) entegre edilirler. Bu, tür etiketlerinin derleyicinin belirli yönlerine erişmesini sağlar, örneğin Types.scala(türlerin birlikte çalışması için nasıl desteklendiğini bilen 7kloc kod), Symbols.scala(sembol tablolarının nasıl çalıştığını bilen 3kloc kod), vb.
Eugene Burmako

9
6) ClassTagtam açılan için yedek ClassManifestise, TypeTagdaha fazla ya da daha az bir ikame maddesidir Manifest. Aşağı yukarı, çünkü: 1) tip etiketleri silme taşımaz, 2) manifestler büyük bir hack'tir ve davranışını tip etiketleri ile taklit ettik. # 1, hem silmeye hem de türe ihtiyacınız olduğunda hem ClassTag hem de TypeTag bağlam sınırları kullanılarak düzeltilebilir ve biri genellikle # 2 ile ilgilenmez, çünkü tüm saldırıları atmak ve tam teşekküllü yansıma API'sını kullanmak mümkün olur yerine.
Eugene Burmako

11
Scala derleyicisinin bir noktada mevcut özelliklerin setini daha dikey hale getirmek için kullanım dışı özelliklerden kurtulacağını umuyorum. Bu yüzden yeni makro desteğini seviyorum çünkü dili temizleme, temel kütüphanenin bir parçası olmayan bağımsız kütüphanelerdeki bazı özellikleri ayırma potansiyeli sunuyor.
Alexandru Nedelcu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.