स्केल: एक टाइपटैग क्या है और मैं इसका उपयोग कैसे करूं?


361

टाइपटैग्स के बारे में मुझे बस इतना पता है कि उन्होंने किसी तरह मैनिफ़ेस्ट को बदल दिया था। इंटरनेट पर जानकारी दुर्लभ है और मुझे इस विषय की अच्छी समझ प्रदान नहीं करती है।

इसलिए मुझे खुशी होगी अगर कोई उदाहरण और लोकप्रिय उपयोग के मामलों सहित टाइपटेग्स पर कुछ उपयोगी सामग्रियों का लिंक साझा करे। विस्तृत उत्तर और स्पष्टीकरण भी स्वागत योग्य हैं।


1
स्काला डॉक्यूमेंटेशन के निम्नलिखित लेख में टाइप टैग्स के क्या और क्यों दोनों का वर्णन किया गया है, साथ ही साथ उन्हें अपने कोड में कैसे उपयोग किया जाए: docs.scala-lang.org/overviews/reflection/…
btiernay

जवाबों:


563

एक 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> 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

इस प्रकार, उन्हें टाइपटेग्स द्वारा प्रतिस्थापित किया जाता है , जो कि उपयोग करने के लिए बहुत सरल हैं और नए प्रतिबिंब एपीआई में अच्छी तरह से एकीकृत हैं। उनके साथ हम उपर्युक्त मार्ग पर निर्भर प्रकार से समस्या को हल कर सकते हैं:

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$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

एक देख सकते हैं, विधि tpeका TypeTagएक पूर्ण में परिणाम 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
    ???
  }
}

यदि एक त्रुटि के 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$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]
                                                      ^

इस प्रश्न के बीच अंतर के बारे में अधिक विस्तृत विवरण के लिए TypeTagऔर WeakTypeTagइस प्रश्न को देखें: स्काला मैक्रोज़: "टाइप टी से अनसुलझे टाइप पैरामीटर वाले टाइपटेग नहीं बना सकते हैं"

स्काला की आधिकारिक प्रलेखन साइट में परावर्तन के लिए एक गाइड भी है ।


19
आपके उत्तर के लिए धन्यवाद! कुछ टिप्पणियाँ: 1) ==प्रकार के लिए संरचनात्मक समानता का प्रतिनिधित्व करता है, संदर्भ समानता नहीं। =:=खाते के प्रकार के समीकरणों (यहां तक ​​कि गैर-स्पष्ट वाले जैसे कि उपसर्गों के समकक्ष जो विभिन्न दर्पणों से आते हैं) को ध्यान में रखें, 2) दोनों TypeTagऔर AbsTypeTagदर्पण पर आधारित हैं। अंतर यह है कि TypeTagकेवल पूरी तरह से त्वरित प्रकार (अर्थात किसी भी प्रकार के पैरामीटर या संदर्भ प्रकार के सदस्यों के बिना) की अनुमति देता है, 3) एक विस्तृत विवरण यहां है: stackoverflow.com/questions/12093752
यूजीन बर्माको

10
4) मैनिफेस्ट में बहुत सारे उपयोगी प्रकारों का प्रतिनिधित्व नहीं करने की समस्या है। अनिवार्य रूप से वे केवल टाइप रिफ्स (सादा प्रकार जैसे कि Intऔर सामान्य प्रकार जैसे List[Int]) व्यक्त कर सकते हैं , ऐसे स्केल प्रकारों को छोड़कर जैसे कि शोधन, पथ-निर्भर प्रकार, अस्तित्व, एनोटेट प्रकार। इसके अलावा मेनिफेस्ट्स एक बोल्ट हैं, इसलिए वे उस विशाल ज्ञान का उपयोग नहीं कर सकते हैं जो संकलक का कहना है, एक प्रकार के रैखिककरण की गणना करें, पता करें कि क्या एक प्रकार दूसरे को उप-प्रकार करता है, आदि
यूजीन बर्माको

9
5) इसके विपरीत प्रकार के टैग "बेहतर एकीकृत" नहीं हैं, वे बस नए प्रतिबिंब एपीआई (मैनिफेस्टों के विपरीत जो कुछ भी एकीकृत नहीं हैं) के साथ एकीकृत हैं। यह संकलक के कुछ पहलुओं को टाइप टैग प्रदान करता है, उदाहरण के लिए Types.scala(कोड के 7kloc जो जानता है कि कैसे एक साथ काम करने के लिए समर्थित हैं), Symbols.scala(कोड का 3kloc कि प्रतीक टेबल कैसे काम करता है), आदि
यूजीन बर्माको

9
6) के ClassTagलिए एक सटीक ड्रॉप-इन प्रतिस्थापन है ClassManifest, जबकि TypeTagकमोबेश इसका विकल्प है Manifest। कम या ज्यादा, क्योंकि: 1) टाइप टैग इरैसरी नहीं करते हैं, 2) मैनिफेस्ट एक बड़ी हैक है, और हमने टाइप टैग के साथ इसके व्यवहार का अनुकरण करना छोड़ दिया। # क्लासगैग और टाइपटैग संदर्भ सीमा दोनों का उपयोग करके आप को ठीक किया जा सकता है, जब आपको इरेज़र और प्रकार दोनों की आवश्यकता होती है, और आमतौर पर # 2 की परवाह नहीं की जाती है, क्योंकि सभी हैक्स को फेंकना और पूर्ण विकसित प्रतिबिंब एपीआई का उपयोग करना संभव हो जाता है। बजाय।
यूजीन बर्माको

11
मुझे वास्तव में उम्मीद है कि स्काला संकलक कुछ बिंदुओं पर उपलब्ध सुविधाओं से छुटकारा पा लेंगे, उपलब्ध सुविधाओं के सेट को अधिक रूढ़िवादी बनाने के लिए। यही कारण है कि मुझे नया मैक्रोज़ समर्थन पसंद है क्योंकि यह भाषा को साफ करने की क्षमता प्रदान करता है, स्वतंत्र पुस्तकालयों में कुछ विशेषताओं को अलग करता है जो आधार भाषा का हिस्सा नहीं हैं।
अलेक्जेंड्रू नेडेल्क्यू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.