15

के साथ संग्रह के लिए स्कैला में न्यूनतम ढांचा, मान लीजिए कि कोई उपन्यास जेनेरिक क्लास, Novel[A] बनाना चाहता है। इस कक्षा में बहुत सारी उपयोगी विधियां होंगी - शायद यह एक प्रकार का संग्रह है - और इसलिए आप इसे उपclass करना चाहते हैं। लेकिन आप विधियों को उप-वर्ग के प्रकार को वापस करने के लिए चाहते हैं, मूल प्रकार नहीं। स्कैला 2.8 में, कम से कम काम करने के लिए क्या करना है ताकि उस वर्ग के तरीके प्रासंगिक उपclass वापस कर सकें, मूल नहीं? उदाहरण के लिए,विरासत वापसी प्रकार

class Novel[A] /* What goes here? */ { 
    /* Must you have stuff here? */ 
    def reverse/* What goes here instead of :Novel[A]? */ = //... 
    def revrev/*?*/ = reverse.reverse 
} 
class ShortStory[A] extends Novel[A] /* What goes here? */ { 
    override def reverse: /*?*/ = //... 
} 
val ss = new ShortStory[String] 
val ss2 = ss.revrev // Type had better be ShortStory[String], not Novel[String] 

अगर आप चाहते हैं Novel covariant जा करने के लिए इस कम से कम राशि परिवर्तन करता है?

(2.8 संग्रह अन्य चीजों के साथ ऐसा करते हैं, लेकिन वे वापसी के प्रकारों के साथ अधिक फैंसी (और उपयोगी) तरीकों से भी खेलते हैं - सवाल यह है कि अगर कोई इस उपप्रकार को हमेशा चाहता है तो कितना छोटा ढांचा दूर हो सकता है -ट्रर्न-उपप्रकार सुविधा।)

संपादित करें: reverse से ऊपर दिए गए कोड में मान लें एक प्रतिलिपि बनाता है। यदि कोई स्थान परिवर्तन करता है और फिर स्वयं को लौटाता है, तो कोई this.type का उपयोग कर सकता है, लेकिन यह काम नहीं करता है क्योंकि प्रति this नहीं है।

अर्जन एक और सवाल है कि निम्नलिखित समाधान का सुझाव से जुड़ा हुआ:

def reverse: this.type = { 
    /*creation of new object*/.asInstanceOf[this.type] 
} 

जो मूल रूप से क्रम में प्रकार व्यवस्था करने के लिए निहित है कि हम क्या चाहते हैं। लेकिन यह वास्तव में एक समाधान नहीं है, क्योंकि अब हमने टाइप सिस्टम से झूठ बोला है, संकलक यह सुनिश्चित करने में हमारी सहायता नहीं कर सकता है कि हम वास्तव में को ShortStory प्राप्त करें जब हमें लगता है कि हम करते हैं। (उदाहरण के लिए, हमें कंपाइलर को खुश करने के लिए ऊपर दिए गए उदाहरण में reverse को ओवरराइड करना नहीं होगा, लेकिन हमारे प्रकार हम जो चाहते थे वह नहीं होंगे।)

+3

शायद आपको स्काला के सिंगलटन प्रकारों का मतलब यह नहीं था कि आपके मन में न्यूनतम ढांचे का उदाहरण है, मुझे लगता है? किसी भी मामले में, उदाहरण के लिए ऑनलाइन उदाहरणों का उपयोग कैसे किया जा सकता है http://scalada.blogspot.com/2008/02/thistype-for-chaining-method-calls.html पर पाया जा सकता है। एसओ पर एक उदाहरण भी है जो दिखाता है कि कुछ हैकरी के साथ अपरिवर्तनीय कक्षाएं कैसे बनाई जा सकती हैं http://stackoverflow.com/questions/775312/why-cannot-this-type-be-used-for-new-instances –

+0

@ अरजन - मेरा मतलब यह नहीं था, क्योंकि यह बहुत सीमित है। (उदाहरण के लिए कॉपी विधियों को प्रतिबंधित किया गया है।) –

उत्तर

3

मैं पूरी तरह से के माध्यम से इस बारे में नहीं सोचा है, लेकिन यह टाइप चेक:

object invariant { 
    trait Novel[A] { 
    type Repr[X] <: Novel[X] 

    def reverse: Repr[A] 

    def revrev: Repr[A]#Repr[A] 
     = reverse.reverse 
    } 
    class ShortStory[A] extends Novel[A] { 
    type Repr[X] = ShortStory[X] 

    def reverse = this 
    } 

    val ss = new ShortStory[String] 
    val ss2: ShortStory[String] = ss.revrev 
} 

object covariant { 
    trait Novel[+A] { 
    type Repr[X] <: Novel[_ <: X] 

    def reverse: Repr[_ <: A] 

    def revrev: Repr[_ <: A]#Repr[_ <: A] = reverse.reverse 
    } 

    class ShortStory[+A] extends Novel[A] { 
    type Repr[X] = ShortStory[X] 

    def reverse = this 
    } 

    val ss = new ShortStory[String] 
    val ss2: ShortStory[String] = ss.revrev 
} 

संपादित

सह संस्करण संस्करण बहुत अच्छे हो सकते हैं:

object covariant2 { 
    trait Novel[+A] { 
    type Repr[+X] <: Novel[X] 

    def reverse: Repr[A] 

    def revrev: Repr[A]#Repr[A] = reverse.reverse 
    } 

    class ShortStory[+A] extends Novel[A] { 
    type Repr[+X] = ShortStory[X] 

    def reverse = this 
    } 

    val ss = new ShortStory[String] 
    val ss2: ShortStory[String] = ss.revrev 
} 
+0

यह आशाजनक लग रहा है, लेकिन अगर मैं 'शॉर्टस्टोरी' को उपclass करना चाहता हूं तो क्या होगा? मुद्दा यह है कि मेरे पास एक वर्ग में उपयोगी प्रारंभकर्ता कोड हो सकता है और उस काम को खोए बिना इसे उपclass करना चाहता है - इसलिए मैं सब कुछ गुणों से उतरना पसंद नहीं करूंगा। हालांकि, अगर जवाब यह है कि एक _can't_ टाइप सिस्टम को मेरी मांगों से सहमत नहीं है, तो यह एक उचित सांत्वना पुरस्कार की तरह दिखता है। –

5

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

शायद नहीं सुंदर, लेकिन एक सरल सार सदस्य प्रकार का उपयोग कर उदाहरण इस प्रकार लागू किया जा सकता:


trait Novel[A] { 
    type T <: Novel[A] 
    def reverse : T 
    def revrev : T#T = reverse.reverse 
} 

class ShortStory[A](var story: String) extends Novel[A] { 
type T = ShortStory[A] 
def reverse : T = new ShortStory[A](story reverse) 
def myMethod: Unit = println("a short story method") 
} 

scala> val ss1 = new ShortStory[String]("the story so far") 
ss1: ShortStory[String] = [email protected] 

scala> val ssRev = ss1 reverse 
ssRev: ss1.T = [email protected] 

scala> ssRev story 
res0: String = raf os yrots eht 

scala> val ssRevRev = ss1 revrev 
ssRevRev: ss1.T#T = [email protected] 

scala> ssRevRev story 
res1: String = the story so far 

scala> ssRevRev myMethod 
a short story method 

यह निश्चित रूप से कम से कम है, लेकिन मुझे शक है यह पर्याप्त होगा कि क्या ढांचे का एक प्रकार के रूप में इस्तेमाल किया जाना है। और निश्चित रूप से प्रकार स्कैला संग्रह ढांचे के रूप में स्पष्ट रूप से कहीं भी वापस नहीं लौटे, तो शायद यह थोड़ा सा सरल हो सकता है। दिए गए मामले के लिए, ऐसा लगता है कि यह काम करता है। जैसा ऊपर बताया गया है, यह दिए गए मामले के लिए नौकरी नहीं करता है, इसलिए यहां कुछ अन्य समाधान की आवश्यकता है।

फिर भी एक और संपादित करें: कुछ इसी तरह के रूप में अच्छी तरह एक ठोस वर्ग उपयोग किया जा सकता है, हालांकि वह भी नहीं सुरक्षित प्रकार होना पर्याप्त होता:


class Novel[A](var story: String) { 
    type T <: Novel[A] 
    def reverse: T = new Novel[A](story reverse).asInstanceOf[T] 
    def revrev : T#T = reverse.reverse 
} 
class ShortStory[A](var s: String) extends Novel[A](s) { 
type T = ShortStory[A] 
override def reverse : T = new ShortStory(story reverse) 
def myMethod: Unit = println("a short story method") 
} 

और कोड विशेषता उदाहरण के रूप में काम करेंगे। लेकिन यह उसी समस्या से पीड़ित है जैसे रेक्स ने अपने संपादन में भी उल्लेख किया है। इस संकलन को बनाने के लिए शॉर्टस्टोरी पर ओवरराइड आवश्यक नहीं है। हालांकि, अगर आप ऐसा नहीं करते हैं और शॉर्टस्टोरी इंस्टेंस पर रिवर्स विधि को कॉल करते हैं तो यह रनटाइम पर असफल हो जाएगा।

+0

अच्छा प्रयास करें, भले ही यह काफी नहीं करता! मुझे यकीन नहीं है कि मैंने जो कुछ पूछा है वह वर्तमान प्रकार प्रणाली के साथ संभव है। (स्कैला संग्रह लक्षणों का एक विशाल ढेर और बहुत कम तत्काल कक्षाओं का उपयोग करता है।) –

2

के बाद स्कैला मेलिंग सूची पर चर्चा - मुझे सही रास्ते पर स्थापित करने के लिए लोगों के लिए बहुत धन्यवाद! - मुझे लगता है कि यह निकटतम है कि कोई न्यूनतम हो सकता है अल ढांचे। मैं इसे यहाँ संदर्भ के लिए छोड़ देते हैं, और मैं एक अलग उदाहरण का उपयोग कर रहा है क्योंकि यह प्रकाश डाला गया है कि क्या हो रहा है बेहतर:

abstract class Peano[A,MyType <: Peano[A,MyType]](a: A, f: A=>A) { 
    self: MyType => 
    def newPeano(a: A, f: A=>A): MyType 
    def succ: MyType = newPeano(f(a),f) 
    def count(n: Int): MyType = { 
    if (n<1) this 
    else if (n==1) succ 
    else count(n-1).succ 
    } 
    def value = a 
} 

abstract class Peano2[A,MyType <: Peano2[A,MyType]](a: A, f: A=>A, g: A=>A) extends Peano[A,MyType](a,f) { 
    self: MyType => 
    def newPeano2(a: A, f: A=>A, g: A=>A): MyType 
    def newPeano(a: A, f: A=>A): MyType = newPeano2(a,f,g) 
    def pred: MyType = newPeano2(g(a),f,g) 
    def uncount(n: Int): MyType = { 
    if (n < 1) this 
    else if (n==1) pred 
    else uncount(n-1).pred 
    } 
} 

कुंजी यहाँ MyType प्रकार पैरामीटर के अलावा के प्रकार के लिए एक प्लेसहोल्डर है कि है वह कक्षा जिसे हम वास्तव में खत्म कर देंगे। प्रत्येक बार जब हम वारिस करते हैं, तो हमें इसे एक प्रकार पैरामीटर के रूप में फिर से परिभाषित करना होगा, और हमने एक कन्स्ट्रक्टर विधि जोड़ दी है जो इस प्रकार की एक नई वस्तु तैयार करेगी। यदि निर्माता बदलता है, तो हमें एक नई कन्स्ट्रक्टर विधि बनाना होगा।

अब जब आप एक वर्ग वास्तव में उपयोग करने के लिए बनाना चाहते हैं, आप केवल नए के लिए एक कॉल के साथ निर्माता विधि को भरने के लिए है (और वर्ग है कि यह अपने आप ही प्रकार का है बताओ):

class Peano2Impl[A](a: A, f: A=>A, g: A=>A) extends Peano2[A,Peano2Impl[A]](a,f,g) { 
    def newPeano2(a: A, f: A=>A, g: A=>A) = new Peano2Impl[A](a,f,g) 
} 

और आप बंद कर रहे हैं और चल रहा है:

val p = new Peano2Impl(0L , (x:Long)=>x+1 , (y:Long)=>x-1) 

scala> p.succ.value 
res0: Long = 1 

scala> p.pred.value 
res1: Long = -1 

scala> p.count(15).uncount(7).value 
res2: Long = 8 

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

यह रणनीति A में कॉन्वर्सिस के लिए ठीक काम करती है, सिवाय इसके कि यह विशेष उदाहरण f और g से काम नहीं कर रहा है।

संबंधित मुद्दे