scala - मैं स्कैला पर चारों ओर मिरर कैसे प्राप्त करूं? या, मुझे अपने संग्रह का प्रकार पैरामीटर क्यों नहीं मिल सकता है?




type-erasure (8)

यह स्कैला पर जीवन का एक दुखद तथ्य है कि यदि आप एक सूची [Int] को तुरंत चालू करते हैं, तो आप यह सत्यापित कर सकते हैं कि आपका उदाहरण एक सूची है, और आप यह सत्यापित कर सकते हैं कि इसका कोई भी व्यक्तिगत तत्व एक इंट है, लेकिन यह नहीं कि यह एक सूची है [ Int], जैसा कि आसानी से सत्यापित किया जा सकता है:

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

अनचेक विकल्प टाइप एरर पर स्क्वायरली रूप से दोष डालता है:

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

वह क्यों है, और मैं इसके आसपास कैसे हो सकता हूं?


यह उत्तर Manifest -API का उपयोग करता है, जिसे स्कैला 2.10 के रूप में बहिष्कृत किया गया है। अधिक मौजूदा समाधानों के लिए कृपया नीचे दिए गए उत्तरों देखें।

स्कैला को टाइप एरर के साथ परिभाषित किया गया था क्योंकि जावा वर्चुअल मशीन (जेवीएम), जावा के विपरीत, जेनेरिक नहीं मिला। इसका मतलब है कि, रन टाइम पर, केवल वर्ग मौजूद है, न कि इसके प्रकार पैरामीटर। उदाहरण में, JVM जानता है कि यह scala.collection.immutable.List को संभालने वाला है, लेकिन यह नहीं कि यह सूची Int साथ पैरामीटरकृत है।

सौभाग्य से, स्कैला में एक सुविधा है जो आपको इसके चारों ओर घूमने देती है। यह प्रकट है । एक अभिव्यक्ति वर्ग है जिसका उदाहरण वस्तुओं का प्रतिनिधित्व करने वाले वस्तुएं हैं। चूंकि ये उदाहरण वस्तुएं हैं, इसलिए आप उन्हें पास कर सकते हैं, उन्हें स्टोर कर सकते हैं, और आम तौर पर उन पर विधियों को कॉल कर सकते हैं। निहित मापदंडों के समर्थन के साथ, यह एक बहुत शक्तिशाली उपकरण बन जाता है। उदाहरण के लिए, निम्न उदाहरण लें:

object Registry {
  import scala.reflect.Manifest

  private var map= Map.empty[Any,(Manifest[_], Any)] 

  def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
    map = map.updated(name, m -> item)
  }

  def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
    map get key flatMap {
      case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
    }     
  }
}

scala> Registry.register("a", List(1,2,3))

scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))

scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None

तत्व को संग्रहीत करते समय, हम इसके "मैनिफेस्ट" को भी स्टोर करते हैं। एक घोषणापत्र एक वर्ग है जिसका उदाहरण स्कैला प्रकारों का प्रतिनिधित्व करता है। इन ऑब्जेक्ट्स में JVM की तुलना में अधिक जानकारी है, जो हमें पूर्ण, पैरामीटर प्रकार के लिए परीक्षण करने में सक्षम बनाता है।

नोट, हालांकि, एक Manifest अभी भी एक विकसित विशेषता है। इसकी सीमाओं के उदाहरण के रूप में, वर्तमान में यह भिन्नता के बारे में कुछ भी नहीं जानता है, और मानता है कि सब कुछ सह-भिन्नता है। मुझे आशा है कि स्काला प्रतिबिंब पुस्तकालय, वर्तमान में विकास के तहत, समाप्त होने के बाद यह अधिक स्थिर और ठोस हो जाएगा।


आप टाइपटैग का उपयोग करके ऐसा कर सकते हैं (जैसा कि डैनियल पहले से ही उल्लेख करता है, लेकिन मैं इसे स्पष्ट रूप से स्पेल कर दूंगा):

import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}

आप क्लासटैग का उपयोग करके भी ऐसा कर सकते हैं (जो आपको स्कैला-प्रतिबिंबित करने पर निर्भर करता है):

import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
  case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}

क्लासटैग का उपयोग तब तक किया जा सकता है जब आप टाइप पैरामीटर A अपेक्षा नहीं करते हैं कि यह एक सामान्य प्रकार हो।

दुर्भाग्यवश यह एक छोटी वर्बोज़ है और आपको एक कंपाइलर चेतावनी दबाने के लिए @unchecked एनोटेशन की आवश्यकता है। टाइपटाग को भविष्य में कंपाइलर द्वारा स्वचालित रूप से पैटर्न मिलान में शामिल किया जा सकता है: https://issues.scala-lang.org/browse/SI-6517


चूंकि जावा वास्तविक तत्व प्रकार को नहीं जानता है, इसलिए मुझे List[_] उपयोग करने के लिए यह सबसे उपयोगी लगता है। तब चेतावनी दूर हो जाती है और कोड वास्तविकता का वर्णन करता है - यह अज्ञात चीज़ की एक सूची है।


परिणाम के बाद आप टाइप करने Typeable टाइप क्लास को Typeable उपयोग कर सकते हैं,

नमूना आरईपीएल सत्र,

scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._

scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)

scala> l1.cast[List[String]]
res0: Option[List[String]] = None

scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))

cast ऑपरेशन यथासंभव सटीक wrt Typeable रूप में संभव होगा, इन-स्कोप Typeable करने Typeable उदाहरण उपलब्ध हैं।


मुझे अन्यथा भयानक भाषा की इस सीमा के लिए थोड़ा बेहतर कामकाज मिला।

स्कैला में, टाइप एरर का मुद्दा एरे के साथ नहीं होता है। मुझे लगता है कि इसे एक उदाहरण के साथ प्रदर्शित करना आसान है।

आइए मान लें कि हमारे पास (Int, String) की एक सूची है, फिर निम्न प्रकार की चेतावनी चेतावनी देता है

x match {
  case l:List[(Int, String)] => 
  ...
}

इसके आसपास काम करने के लिए, पहले केस क्लास बनाएं:

case class IntString(i:Int, s:String)

फिर पैटर्न मिलान में कुछ ऐसा करते हैं:

x match {
  case a:Array[IntString] => 
  ...
}

जो पूरी तरह से काम करता प्रतीत होता है।

सूचियों की बजाय सरणी के साथ काम करने के लिए इसके लिए आपके कोड में मामूली परिवर्तन की आवश्यकता होगी, लेकिन यह एक बड़ी समस्या नहीं होनी चाहिए।

ध्यान दें कि case a:Array[(Int, String)] का उपयोग करके case a:Array[(Int, String)] अभी भी एक प्रकार की मिरर चेतावनी देगा, इसलिए एक नए कंटेनर क्लास (इस उदाहरण में, IntString ) का उपयोग करना आवश्यक है।


मैं एक अपेक्षाकृत सरल समाधान के साथ आया जो सीमित उपयोग स्थितियों में पर्याप्त होगा, अनिवार्य रूप से पैरामीटरयुक्त प्रकारों को लपेटना जो रैपर वर्गों में टाइप एरर समस्या से ग्रस्त होंगे, जिनका मिलान मैच स्टेटमेंट में किया जा सकता है।

case class StringListHolder(list:List[String])

StringListHolder(List("str1","str2")) match {
    case holder: StringListHolder => holder.list foreach println
}

इसने अपेक्षित आउटपुट और हमारे केस क्लास की वांछित प्रकार, स्ट्रिंग सूचियों की सामग्री को सीमित कर दिया है।

यहां अधिक जानकारी: http://www.scalafied.com/?p=60


मैं सोच रहा हूं कि यह एक उपयुक्त कामकाज है:

scala> List(1,2,3) match {
     |    case List(_: String, _*) => println("A list of strings?!")
     |    case _ => println("Ok")
     | }

यह "रिक्त सूची" केस से मेल नहीं खाता है, लेकिन यह एक संकलन त्रुटि देता है, चेतावनी नहीं!

error: type mismatch;
found:     String
requirerd: Int

दूसरी ओर यह काम करने लगता है ....

scala> List(1,2,3) match {
     |    case List(_: Int, _*) => println("A list of ints")
     |    case _ => println("Ok")
     | }

क्या यह थोड़ी बेहतर नहीं है या क्या मैं यहां बिंदु खो रहा हूं?


स्कैला में टाइप एरर इश्यू को दूर करने का एक तरीका है। मिलान 1 में ओवरराइंग टाइप एरर में और मिलान 2 (भिन्नता) में टाइप एरर पर काबू पाने में कुछ स्पष्टीकरण हैं कि कुछ मददगारों को मिलान करने के लिए वेरिएंस समेत प्रकारों को लपेटने के लिए कैसे कोड करें।







type-erasure