उदाहरण संकलित क्यों नहीं किया जाता है, उर्फ ​​कैसे (सह-, गर्भनिरोधक, और-) विचरण कार्य करता है?


147

से इसे जारी रखते हुए इस सवाल का , कर सकते हैं किसी को स्काला में निम्नलिखित व्याख्या करते हैं:

class Slot[+T] (var some: T) { 
   //  DOES NOT COMPILE 
   //  "COVARIANT parameter in CONTRAVARIANT position"

}

मैं के बीच के अंतर को समझने +Tऔर Tप्रकार घोषणा (अगर मैं का उपयोग यह संकलित में T)। लेकिन तब कोई वास्तव में एक वर्ग को कैसे लिखता है जो अपने प्रकार के पैरामीटर में सहसंयोजक होता है बिना किसी चीज के अनपेक्षित रूप से बनाने के लिए ? मैं यह कैसे सुनिश्चित कर सकता हूं कि निम्नलिखित केवल एक उदाहरण के साथ बनाया जा सकता है T?

class Slot[+T] (var some: Object){    
  def get() = { some.asInstanceOf[T] }
}

EDIT - अब इसे निम्न के लिए मिला है:

abstract class _Slot[+T, V <: T] (var some: V) {
    def getT() = { some }
}

यह सब अच्छा है, लेकिन मेरे पास अब दो प्रकार के पैरामीटर हैं, जहां मुझे केवल एक ही चाहिए। मैं इस प्रकार सवाल फिर से पूछूँगा:

मैं एक अपरिवर्तनीय Slot वर्ग कैसे लिख सकता हूं जो अपने प्रकार में सहसंयोजक है?

संपादित 2 : Duh! मैंने इस्तेमाल किया varऔर नहीं val। निम्नलिखित वह है जो मैं चाहता था:

class Slot[+T] (val some: T) { 
}

6
क्योंकि varसुलझने valयोग्य नहीं है। यह वही कारण है कि स्केला के अपरिवर्तनीय संग्रह सहसंयोजक हैं, लेकिन उत्परिवर्तनीय नहीं हैं।
Oxbow_lakes

यह इस संदर्भ में दिलचस्प हो सकता है: scala-lang.org/old/node/129
user573215

जवाबों:


302

आम तौर पर, एक सहसंयोजक प्रकार पैरामीटर वह होता है जिसे अलग-अलग करने की अनुमति दी जाती है क्योंकि वर्ग को उप-प्रकार दिया जाता है (वैकल्पिक रूप से, उपप्रकार के साथ भिन्न होता है, इसलिए "सह-" उपसर्ग)। अधिक संक्षिप्त:

trait List[+A]

List[Int]का उप-प्रकार है List[AnyVal]क्योंकि Intइसका उप-प्रकार है AnyVal। इसका मतलब है कि आप एक उदाहरण प्रदान कर सकते हैं List[Int]जब एक प्रकार List[AnyVal]का मूल्य अपेक्षित होता है। जेनरिक के लिए काम करने के लिए यह वास्तव में एक बहुत ही सहज तरीका है, लेकिन यह पता चलता है कि यह उत्परिवर्तित है (टाइप सिस्टम को तोड़ता है) जब उत्परिवर्तनीय डेटा की उपस्थिति में उपयोग किया जाता है। यही कारण है कि जावा में जेनेरिक अपरिवर्तनीय हैं। जावा सरणियों (जो त्रुटिपूर्ण रूप से सहसंयोजक हैं) का उपयोग करते हुए बेईमानी का संक्षिप्त उदाहरण:

Object[] arr = new Integer[1];
arr[0] = "Hello, there!";

हमने केवल एक प्रकार के प्रकार Stringको एक मान दिया हैInteger[] । जिन कारणों से यह स्पष्ट होना चाहिए, यह बुरी खबर है। जावा का टाइप सिस्टम वास्तव में संकलन के समय इसकी अनुमति देता है। JVM ArrayStoreExceptionरन टाइम पर "मददगार" होगा । स्काला का प्रकार सिस्टम इस समस्या को रोकता है क्योंकि Arrayवर्ग पर टाइप पैरामीटर अपरिवर्तनीय है (घोषणा इसके [A]बजाय है [+A])।

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

trait Function1[-P, +R] {
  def apply(p: P): R
}

प्रकार पैरामीटर पर " - " विचरण एनोटेशन पर ध्यान दें P। यह घोषणा एक पूरे साधन के रूप में होती है, जिसमें Function1अंतर्विरोधी Pऔर अंदर सहसंयोजक होता है R। इस प्रकार, हम निम्नलिखित स्वयंसिद्ध व्युत्पन्न कर सकते हैं:

T1' <: T1
T2 <: T2'
---------------------------------------- S-Fun
Function1[T1, T2] <: Function1[T1', T2']

ध्यान दें कि T1'एक उपप्रकार (या उसी प्रकार का) होना चाहिए T1, जबकि यह इसके लिए विपरीत है T2और T2'। अंग्रेजी में, इसे निम्नलिखित के रूप में पढ़ा जा सकता है:

एक फ़ंक्शन A दूसरे फ़ंक्शन B का उपप्रकार है, यदि A का पैरामीटर प्रकार B के प्रकार का पैरामीटर है, जबकि A का वापसी प्रकार B के प्रकार का एक उपप्रकार है ।

इस नियम का कारण पाठक को एक अभ्यास के रूप में छोड़ दिया गया है (संकेत: विभिन्न मामलों के बारे में सोचें क्योंकि फ़ंक्शन उप-प्रकार हैं, जैसे ऊपर से मेरा सरणी उदाहरण)।

सह-और विरोधाभासी के अपने नए-ज्ञान के साथ, आपको यह देखने में सक्षम होना चाहिए कि निम्नलिखित उदाहरण क्यों संकलित नहीं होंगे:

trait List[+A] {
  def cons(hd: A): List[A]
}

समस्या यह है कि Aसहसंयोजक है, जबकि consफ़ंक्शन अपेक्षा करता है कि इसका प्रकार पैरामीटर अपरिवर्तनीय है । इस प्रकार, Aगलत दिशा बदलती है। दिलचस्प रूप से पर्याप्त है, हम इस समस्या को Listकॉन्ट्रावरीएन्ट बनाकर हल कर सकते हैं A, लेकिन फिर रिटर्न प्रकार List[A]अमान्य होगा क्योंकि consफ़ंक्शन को उम्मीद है कि उसका रिटर्न प्रकार सहसंयोजक होगा

हमारे यहाँ केवल दो विकल्प हैं) एक Aअपरिवर्तनीय बनाते हैं , कोवरियन के अच्छे, सहज उप-टाइपिंग गुण खो देते हैं, या ख) एक स्थानीय प्रकार के पैरामीटर को उस consविधि में जोड़ते हैं जो Aएक निचली सीमा के रूप में परिभाषित करता है :

def cons[B >: A](v: B): List[B]

यह अब मान्य है। आप कल्पना कर सकते हैं कि Aयह नीचे की ओर अलग है, लेकिन इसकी निचली सीमा के बाद से Bऊपर की ओर अलग-अलग Aहो सकता Aहै। इस पद्धति की घोषणा के साथ, हम Aसहसंयोजक हो सकते हैं और सब कुछ काम करता है।

ध्यान दें कि यह ट्रिक केवल तभी काम करती है जब हम एक ऐसे उदाहरण को वापस करते हैं Listजो कम-विशिष्ट प्रकार पर विशेष होता है B। यदि आप Listपरिवर्तनशील बनाने की कोशिश करते हैं , तो चीजें खत्म हो जाती हैं क्योंकि आप प्रकार के मूल्यों Bको एक चर के प्रकार को निर्दिष्ट करने की कोशिश कर रहे हैं A, जो संकलक द्वारा अस्वीकृत है। जब भी आपके पास उत्परिवर्तन होता है, तो आपको कुछ प्रकार के एक म्यूटेटर की आवश्यकता होती है, जिसके लिए एक निश्चित प्रकार के एक विधि पैरामीटर की आवश्यकता होती है, जो (एक्सेसर के साथ) अव्यवस्था का अर्थ है। सहसंयोजक अपरिवर्तनीय डेटा के साथ काम करता है क्योंकि एकमात्र संभव संचालन एक सहायक है, जिसे एक सहसंयोजक रिटर्न प्रकार दिया जा सकता है।


4
क्या इसे सादे अंग्रेजी में कहा जा सकता है - आप एक पैरामीटर के रूप में कुछ और सरल ले सकते हैं और आप कुछ और जटिल कर सकते हैं?
फिलऑक्ट

1
जावा कंपाइलर (1.7.0) "ऑब्जेक्ट [] अरेस्ट = नया इंट [1];" बल्कि त्रुटि संदेश देता है: "जावा: असंगत प्रकार की आवश्यकता: java.lang.Object [] पाया: int []"। मुझे लगता है कि आपका मतलब "ऑब्जेक्ट [] गिरफ्तार करना = नया पूर्णांक [1];"।
एम्रे सेविनके

2
जब आपने उल्लेख किया, "इस नियम का कारण पाठक को एक अभ्यास के रूप में छोड़ दिया गया है (संकेत: विभिन्न मामलों के बारे में सोचें क्योंकि फ़ंक्शन उप-प्रकार हैं, जैसे ऊपर से मेरा सरणी उदाहरण)।" क्या आप वास्तव में एक दो उदाहरण दे सकते हैं?
पेरीज़िेंग

2
प्रति @perryzheng इस ले trait Animal, trait Cow extends Animal, def iNeedACowHerder(herder: Cow => Unit, c: Cow) = herder(c)और def iNeedAnAnimalHerder(herder: Animal => Unit, a: Animal) = herder(a)। फिर, iNeedACowHerder({ a: Animal => println("I can herd any animal, including cows") }, new Cow {})ठीक है, जैसा कि हमारे पशु के झुंड गायों का झुंड कर सकते हैं, लेकिन iNeedAnAnimalHerder({ c: Cow => println("I can herd only cows, not any animal") }, new Animal {})एक संकलन त्रुटि देता है, क्योंकि हमारी गाय का झुंड सभी जानवरों को झुंड नहीं दे सकता है।
लसफ

यह संबंधित है और मुझे विचरण के साथ मदद करता है: typelevel.org/blog/2016/02/04/variance-and-functors.html
पीटर

27

@ डैनियल ने इसे बहुत अच्छे से समझाया है। लेकिन इसे संक्षेप में समझाने के लिए, अगर इसकी अनुमति थी:

  class Slot[+T](var some: T) {
    def get: T = some   
  }

  val slot: Slot[Dog] = new Slot[Dog](new Dog)   
  val slot2: Slot[Animal] = slot  //because of co-variance 
  slot2.some = new Animal   //legal as some is a var
  slot.get ??

slot.getतो क्रम में एक त्रुटि फेंक होगा क्योंकि यह एक परिवर्तित करने में असफल रहा था Animalकरने के लिएDog (ओह!)।

सामान्य परिवर्तनशीलता में सह-विचरण और गर्भ-विचरण के साथ अच्छी तरह से नहीं होता है। यही कारण है कि सभी जावा संग्रह अपरिवर्तनीय हैं।


7

उदाहरण के लिए , इस की पूरी चर्चा के लिए पृष्ठ 57+ देखें ।

यदि मैं आपकी टिप्पणी को सही ढंग से समझ रहा हूं, तो आपको पृष्ठ 56 के नीचे से शुरू होने वाले मार्ग को फिर से पढ़ना होगा (मूल रूप से, मुझे लगता है कि आप जो पूछ रहे हैं वह रन-टाइम चेक के बिना टाइप-सुरक्षित नहीं है, जो कि स्कैला नहीं करता है,) तो तुम भाग्य से बाहर हो)। अपने निर्माण का उपयोग करने के लिए उनके उदाहरण का अनुवाद:

val x = new Slot[String]("test") // Make a slot
val y: Slot[Any] = x             // Ok, 'cause String is a subtype of Any
y.set(new Rational(1, 2))        // Works, but now x.get() will blow up 

यदि आपको लगता है कि मैं आपके प्रश्न (एक विशिष्ट संभावना) को नहीं समझ रहा हूँ, तो समस्या के विवरण में और स्पष्टीकरण / संदर्भ जोड़कर देखें और मैं फिर से कोशिश करूँगा।

आपके संपादन के जवाब में: अपरिवर्तनीय स्लॉट्स पूरी तरह से अलग स्थिति हैं ... * मुस्कान * मुझे आशा है कि ऊपर दिए गए उदाहरण से मदद मिली।


मैंने पढ़ा है कि; दुर्भाग्य से मैं (अभी भी) समझ में नहीं आता है कि मैं कैसे कर सकता हूं जो मैं ऊपर पूछता हूं (यानी वास्तव में टी में एक
पैरामीरिज्ड क्लास कोवरिएंट लिखता हूं

मैंने अपना डाउनमार्क हटा दिया क्योंकि मुझे एहसास हुआ कि यह थोड़ा कठोर था। मुझे इस सवाल (स) में स्पष्ट करना चाहिए कि मैंने स्काला से बिट्स को उदाहरण के लिए पढ़ा था; मैं चाहता था कि इसे "कम औपचारिक" तरीके से समझाया जाए
oxbow_lakes

@oxbow_lakes मुस्कान मुझे डर है उदाहरण के लिए स्काला कम औपचारिक स्पष्टीकरण है। सबसे अच्छे रूप में, हम यहाँ पर काम करने के लिए ठोस उदाहरणों का उपयोग करने का प्रयास कर सकते हैं ...
मरकुस

क्षमा करें - मैं नहीं चाहता कि मेरा स्लॉट परिवर्तनशील हो। मैंने अभी महसूस किया है कि समस्या है कि मुझे var घोषित किया गया है और न कि Val
oxbow_lakes

3

आपको पैरामीटर पर एक कम बाध्य लागू करने की आवश्यकता है। मुझे वाक्य रचना को याद करने में मुश्किल समय आ रहा है, लेकिन मुझे लगता है कि यह कुछ इस तरह दिखाई देगा:

class Slot[+T, V <: T](var some: V) {
  //blah
}

स्काला-दर-उदाहरण समझने में थोड़ा कठिन है, कुछ ठोस उदाहरणों ने मदद की होगी।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.