2013-03-03 10 views
49

में अंडरस्कोर बनाम क्या स्काला में निम्नलिखित जेनेरिक्स परिभाषाओं के बीच अलग है:स्केला - कोई जेनरिक

class Foo[T <: List[_]] 

और

class Bar[T <: List[Any]] 

मेरे पेट मुझसे कहता है कि वे एक ही के बारे में, लेकिन वह बाद के हैं अधिक स्पष्ट है। मुझे ऐसे मामले मिल रहे हैं जहां पूर्व संकलित हैं लेकिन बाद वाला नहीं है, लेकिन मेरी अंगुली को सही अंतर पर नहीं डाल सकता है।

धन्यवाद!

संपादित करें:

मैं मिश्रण में एक और फेंक कर सकते हैं?

class Foo[T <: List[Z forSome { type Z }] 

यह फार्म का कहना है कि List के तत्व प्रकार है:

class Baz[T <: List[_ <: Any]] 
+5

एक प्रकार को रोकने के लिए '<: कोई भी' कभी भी कुछ भी नहीं बदलता है। स्कैला में हर प्रकार '<: कोई भी 'है। –

उत्तर

64

ठीक है, मैं समझ रहा टिप्पणियां सिर्फ पोस्टिंग के बजाय उस पर मेरी ले होनी चाहिए,। क्षमा करें, यह लंबा होगा, यदि आप चाहते हैं कि टीएलडीआर अंत तक छोड़ जाए।

के रूप में रान्डेल शुल्ज ने कहा, यहां _ एक अस्तित्व प्रकार के लिए एक आशुलिपि है। अर्थात्,

class Foo[T <: List[_]] 

के लिए

class Foo[T <: List[Z] forSome { type Z }] 

नोट एक आशुलिपि है कि क्या रान्डेल Shulz के जवाब का उल्लेख है के विपरीत (पूर्ण प्रकटीकरण: मैं गलत यह भी इस पोस्ट के लिए किसी पुराने संस्करण में मिला है, जेस्पर Nordenberg करने के लिए धन्यवाद

0:

class Foo[T <: List[Z]] forSome { type Z } 

और न ही यह रूप में एक ही है: यह उनका कहना है) यह एक ही नहीं के रूप में के लिए

सावधान रहें, इसे गलत करना आसान है (मेरे पहले के गोफ शो के रूप में): रैंडल शुल्ज़ के जवाब के संदर्भ में आलेख के लेखक ने इसे गलत पाया (टिप्पणियां देखें), और बाद में इसे ठीक कर दिया। इस आलेख के साथ मेरी मुख्य समस्या यह है कि उदाहरण में दिखाया गया है कि अस्तित्व का उपयोग हमें टाइपिंग समस्या से बचाने के लिए है, लेकिन ऐसा नहीं है। कोड जांचें, और compileAndRun(helloWorldVM("Test")) या compileAndRun(intVM(42)) संकलित करने का प्रयास करें। हां, संकलन नहीं करता है। बस compileAndRunA में जेनेरिक कोड संकलित करेगा, और यह बहुत आसान होगा। संक्षेप में, शायद अस्तित्व के बारे में जानने के लिए यह सबसे अच्छा लेख नहीं है और वे किसके लिए अच्छे हैं (लेखक स्वयं एक टिप्पणी में स्वीकार करते हैं कि लेख "को साफ करने की आवश्यकता है")।

तो मैं इस आलेख को पढ़ने की सिफारिश करता हूं: http://www.artima.com/scalazine/articles/scalas_type_system.html, विशेष रूप से "मौजूदा प्रकार" और "जावा और स्कैला में भिन्नता" नामक अनुभाग।

इस लेख से आपको जो महत्वपूर्ण बिंदु मिलना चाहिए वह यह है कि गैर-कॉन्वेंट प्रकारों से निपटने के दौरान अस्तित्व उपयोगी हैं (जेनेरिक जावा वर्गों से निपटने में सक्षम होने के अलावा)। यहां एक उदाहरण है।

case class Greets[T](private val name: T) { 
    def hello() { println("Hello " + name) } 
    def getName: T = name 
} 

इस वर्ग के सामान्य है (ध्यान दें यह भी कहा कि अपरिवर्तनीय है), लेकिन हम देख सकते हैं कि hello वास्तव में (getName के विपरीत) प्रकार पैरामीटर का उपयोग नहीं करते हैं, इसलिए यदि मैं Greets मैं का एक उदाहरण मिलता है हमेशा इसे कॉल करने में सक्षम होना चाहिए, जो भी T है। मैं एक विधि है कि एक Greets उदाहरण लेता है परिभाषित करना चाहते हैं और सिर्फ अपने hello प्रणाली को बुलाती है, तो मैं इस कोशिश कर सकते:

def sayHi1(g: Greets[T]) { g.hello() } // Does not compile 

पर्याप्त बेशक, इस संकलन नहीं करता है, के रूप में T कहीं नहीं यहाँ से बाहर आता है।

ठीक तो, विधि सामान्य बनाते हैं:

def sayHi2[T](g: Greets[T]) { g.hello() } 
sayHi2(Greets("John")) 
sayHi2(Greets('Jack)) 

बढ़िया है, यह काम करता है। हम यहां अस्तित्व का भी उपयोग कर सकते हैं:

def sayHi3(g: Greets[_]) { g.hello() } 
sayHi3(Greets("John")) 
sayHi3(Greets('Jack)) 

भी काम करता है। तो सब कुछ, अस्तित्व (जैसे sayHi3 में) टाइप पैरामीटर (जैसे sayHi2) का उपयोग करने से यहां कोई वास्तविक लाभ नहीं है।

हालांकि, अगर Greets किसी अन्य जेनेरिक वर्ग में टाइप पैरामीटर के रूप में दिखाई देता है तो यह परिवर्तन होता है। उदाहरण के लिए कहें कि हम एक सूची में Greets (विभिन्न T के साथ) के कई उदाहरणों को स्टोर करना चाहते हैं। यह कोशिश करते हैं:

val greets1: Greets[String] = Greets("John") 
val greets2: Greets[Symbol] = Greets('Jack) 
val greetsList1: List[Greets[Any]] = List(greets1, greets2) // Does not compile 

अंतिम पंक्ति संकलन नहीं है क्योंकि Greets अपरिवर्तनीय है, तो एक Greets[String] और Greets[Symbol] एक Greets[Any] भी String और Symbol हालांकि दोनों फैली Any के रूप में नहीं माना जा सकता।

val greetsList2: List[Greets[_]] = List(greets1, greets2) // Compiles fine, yeah 

यह ठीक संकलित, अपेक्षा के अनुरूप है, और आप कर सकते हैं::

ठीक है, के आशुलिपि संकेतन _ का उपयोग कर एक अस्तित्व के साथ प्रयास करें, चलो

greetsSet foreach (_.hello) 

अब, यही कारण है याद हमारे पास पहली जगह में एक प्रकार की जांच समस्या थी क्योंकि Greets invariant है। यदि इसे एक कॉन्वेंरिएंट क्लास (class Greets[+T]) में बदल दिया गया था तो सब कुछ बॉक्स से बाहर काम करता और हमें कभी भी अस्तित्व की आवश्यकता नहीं होती।


तो योग करने के लिए, existentials सामान्य अपरिवर्तनीय वर्गों से निपटने के लिए उपयोगी होते हैं, लेकिन अगर सामान्य वर्ग एक और सामान्य वर्ग के लिए एक प्रकार पैरामीटर के रूप में प्रकट करने के लिए की जरूरत नहीं है, संभावना है कि आप की जरूरत नहीं है कर रहे हैं existentials और बस अपने विधि करने के लिए एक प्रकार पैरामीटर जोड़ने के बारे में

class Foo[T <: List[_]] 

क्योंकि List covariant है

काम करेंगे, अपने विशिष्ट प्रश्न पर वापस आते हैं (पिछले पर, मुझे पता है!) अब, यह सभी उद्देश्यों के लिए है और purp ओएसई जैसा कह रहा है:

class Foo[T <: List[Any]] 

तो इस मामले में, या तो नोटेशन का उपयोग करना वास्तव में शैली का मामला है।

हालांकि, अगर आप Set साथ List की जगह, चीजों को बदलना:

class Foo[T <: Set[_]] 

Set अपरिवर्तनीय है और इस तरह हम अपना उदाहरण से Greets वर्ग के साथ के रूप में एक ही स्थिति में हैं। इस प्रकार उपरोक्त वास्तव में

class Foo[T <: Set[Any]] 
+0

नहीं, 'कक्षा फू [टी <: सूची [_]] 'वर्ग फू [टी <: सूची [जेड] के लिए कुछ {प्रकार जेड}]' के लिए लघुरूप है। वैसे ही 'सूची [ग्रेट्स [_]] 'सूची [ग्रेट्स [जेड] के लिए कुछ {टाइप जेड}]' (और 'सूची [ग्रेट्स [जेड]] के लिए कुछ {टाइप जेड}' के लिए लघुरूप है। –

+0

दोह, मूर्ख मुझे! धन्यवाद, मैंने इसे ठीक कर दिया है। मजेदार है, मेरा पहला हालांकि डेविड आर मैकइवर (http://www.drmaciver.com/2008/03/existential-types-in-scala/) द्वारा आलेख की जांच करना था जो सटीक अस्तित्व के बारे में बात करता है, और इसके बारे में चेतावनी देता है उनके अनजान desugaring। ऐसा लगता है कि वह खुद को गलत मानता है। दरअसल, क्या हुआ यह है कि जिस तरह से desugaring किया गया है उसके लेख के तुरंत बाद बदल गया (स्केल 2.7.1 में, http://www.scala-lang.org/node/43#2.8.0 पर परिवर्तन लॉग देखें)। मुझे लगता है कि यह परिवर्तन भ्रम में भाग लेता है। –

+0

ठीक है, अस्तित्व के प्रकार वाक्यविन्यास का अर्थ मिश्रण करना आसान है। कम से कम वर्तमान desugaring सबसे तार्किक IMHO है। –

6

पूर्व एक अस्तित्व प्रकार के लिए एक आशुलिपि जब कोड पता है कि किस प्रकार है या विवश यह करने की जरूरत नहीं है आपके दूसरे फॉर्म की बजाय class Foo पर अज्ञात, जो विशेष रूप से List का तत्व प्रकार Any है।

चेक बाहर स्काला में अस्तित्व के प्रकार पर इस संक्षिप्त व्याख्यात्मक blog article

+3

जब आप समझाते हैं कि अस्तित्व क्या है, तो मुझे लगता है कि सवाल यह नहीं है कि अस्तित्व में क्या अस्तित्व सामान्य हैं, लेकिन क्या टी [_] '(जो अस्तित्व के उपयोग का विशेष मामला है) और' टी [कोई भी] के बीच कोई वास्तविक अवलोकन अंतर है। '? –

+0

बेशक वहाँ है। जिस ब्लॉग का मैंने उल्लेख किया है उसका एक अच्छा उदाहरण है। –

+3

मेरे पास ** आपके ** उत्तर का उल्लेख है कि 'टी [_]' और 'टी [किसी भी] 'के बीच के अंतर के लिए कॉन्वर्सिस आवश्यक है, क्योंकि यह सवाल के दिल में है" क्यों टी का उपयोग भी करें [_] 'टी '['] '' '। इसके अलावा, अपने प्रश्न में शॉन कॉनॉली ने स्पष्ट रूप से 'सूची [_] 'का उल्लेख किया। यह देखते हुए कि 'सूची' वास्तव में अनुकूल है, कोई आश्चर्यचकित हो सकता है कि ** इस मामले में ** ** सूची [_] 'और 'सूची [किसी भी]' के बीच वास्तव में एक अंतर है। –