2012-08-11 26 views
8

एक केस क्लास copy() विधि उदाहरण की एक समान प्रतिलिपि बनाना है, साथ ही किसी भी फ़ील्ड को नाम से बदलना है। यह विफल होने लगता है जब केस क्लास में मैनिफ़ेस्ट के साथ टाइप पैरामीटर होते हैं। प्रतिलिपि इसके मानकों के प्रकार के सभी ज्ञान खो देता है।स्कैला: केस क्लास कॉपी को प्रकट जानकारी कैसे बनाएं

case class Foo[+A : Manifest](a: A) { 
    // Capture manifest so we can observe it 
    // A demonstration with collect would work equally well 
    def myManifest = implicitly[Manifest[_ <: A]] 
} 

case class Bar[A <: Foo[Any]](foo: A) { 
    // A simple copy of foo 
    def fooCopy = foo.copy() 
} 

val foo = Foo(1) 
val bar = Bar(foo) 

println(bar.foo.myManifest)  // Prints "Int" 
println(bar.fooCopy.myManifest) // Prints "Any" 

क्यों Foo.copy मानकों के आधार पर प्रकट खोना है और मैं इसे यह बनाए रखने के कैसे कर सकता हूँ?

उत्तर

15

कई स्केल विशिष्टताओं इस व्यवहार को देने के लिए बातचीत करते हैं। पहली बात यह है कि Manifest एस न केवल कन्स्ट्रक्टर पर गुप्त अंतर्निहित पैरामीटर सूची में शामिल हैं बल्कि कॉपी विधि पर भी शामिल हैं। यह सर्वविदित है कि

case class Foo[+A : Manifest](a: A)

के लिए

case class Foo[+A](a: A)(implicit m: Manifest[A])

बस वाक्यात्मक चीनी है, लेकिन यह भी प्रतिलिपि निर्माता है, जो इस

def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)

कैसा दिखेगा को प्रभावित करता है उन सभी implicit m एस वें द्वारा बनाए गए हैं ई संकलक और अंतर्निहित पैरामीटर सूची के माध्यम से विधि को भेजा।

यह तब तक ठीक रहेगा जब तक कोई copy विधि का उपयोग उस स्थान पर कर रहा था जहां संकलक Foo एस प्रकार पैरामीटर जानता था। उदाहरण के लिए, इस बार कक्षा के बाहर काम करेंगे:

val foo = Foo(1) 
val aCopy = foo.copy() 
println(aCopy.myManifest) // Prints "Int" 

यह काम करता है क्योंकि संकलक infers कि foo एक Foo[Int] तो यह जानता है foo.a एक Int तो यह इस तरह copy कॉल कर सकते हैं है:

val aCopy = foo.copy()(manifest[Int]())

(ध्यान दें कि manifest[T]() एक समारोह है कि प्रकार T, जैसे एक राजधानी "एम" के साथ Manifest[T] की एक प्रकट प्रतिनिधित्व बनाता है। नहीं दिखाया गया है एक है copy में डिफ़ॉल्ट पैरामीटर का डीडिशन।) यह Foo कक्षा के भीतर भी काम करता है क्योंकि इसमें पहले से ही अभिव्यक्ति है जिसे कक्षा के निर्माण के दौरान पारित किया गया था। यह कुछ इस तरह दिखेगा:

case class Foo[+A : Manifest](a: A) { 
    def myManifest = implicitly[Manifest[_ <: A]] 

    def localCopy = copy() 
} 

val foo = Foo(1) 
println(foo.localCopy.myManifest) // Prints "Int" 

मूल उदाहरण में, तथापि, यह दूसरा विशेष लक्षण की वजह से Bar कक्षा में विफल रहता है: Bar के प्रकार के मापदंडों Bar वर्ग के भीतर जाना जाता है, जबकि, के प्रकार के पैरामीटर प्रकार पैरामीटर नहीं हैं। यह जानता है कि BarFoo या SubFoo या SubSubFoo है, लेकिन यदि यह Foo[Int] या Foo[String] नहीं है। यह निश्चित रूप से, स्कैला में जाने-माने प्रकार की समस्या है, लेकिन यह एक समस्या के रूप में दिखाई देता है, भले ही ऐसा लगता है कि कक्षा foo एस प्रकार पैरामीटर के प्रकार से कुछ भी कर रही है। लेकिन यह है, याद रखें कि हर बार copy को एक मैनिफेस्ट का एक गुप्त इंजेक्शन कहा जाता है, और वे प्रकट होते हैं जो पहले वहां थे।चूंकि Bar वर्ग है पता नहीं था foo के प्रकार के पैरामीटर, है यह सिर्फ Any की एक प्रकट बनाता है और भेजता है साथ इस तरह:

def fooCopy = foo.copy()(manifest[Any])

यदि एक Foo वर्ग पर नियंत्रण है (उदाहरण के लिए यह नहीं है List) फिर एक एक विधि जोड़कर फू कक्षा में सभी नकल से अधिक कार्य करके वैकल्पिक हल यह है कि इसके बाद के संस्करण localCopy तरह उचित नकल करेंगे, है, और परिणाम लौटने:

case class Bar[A <: Foo[Any]](foo: A) { 
    //def fooCopy = foo.copy() 
    def fooCopy = foo.localCopy 
} 

val bar = Bar(Foo(1)) 
println(bar.fooCopy.myManifest) // Prints "Int" 

एक अन्य समाधान Bar की एक प्रकट प्रकार पैरामीटर के रूप में Foo रों प्रकार पैरामीटर जोड़ने के लिए है:

case class Bar[A <: Foo[B], B : Manifest](foo: A) { 
    def fooCopy = foo.copy() 
} 

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

val bar = Bar(Foo(1)) // Does not compile 

val bar = Bar[Foo[Int], Int](Foo(1)) // Compiles 
+0

अच्छा काम, मेरे दोस्त! –

1

दोनों समस्याओं के रूप में आप की पहचान कर रहे हैं। पहली समस्या Bar के अंदर टाइप एरर समस्या है, जहां BarFoo के मैनिफेस्ट के प्रकार को नहीं जानता है। मैं व्यक्तिगत रूप से सुझाए गए localCopy वर्कअराउंड का उपयोग करता हूं।

दूसरा मुद्दा यह है कि एक और निहितार्थ गुप्त रूप से copy में इंजेक्शन दिया जा रहा है। उस मुद्दे को copy में फिर से मूल्य को स्पष्ट रूप से पारित करके हल किया गया है। उदाहरण के लिए:

scala> case class Foo[+A](a: A)(implicit val m: Manifest[A @uncheckedVariance]) 
defined class Foo 

scala> case class Bar[A <: Foo[Any]](foo: A) { 
    | def fooCopy = foo.copy()(foo.m) 
    | } 
defined class Bar 

scala> val foo = Foo(1) 
foo: Foo[Int] = Foo(1) 

scala> val bar = Bar(foo) 
bar: Bar[Foo[Int]] = Bar(Foo(1)) 

scala> bar.fooCopy.m 
res2: Manifest[Any] = Int 

हम प्रति विलोपन के कारण Int प्रकट लेकिन fooCopy और res2 के प्रकार है Manifest[Any] को रखा गया है देखते हैं।

क्योंकि मुझे copy करने के लिए निहित साक्ष्य तक पहुंच की आवश्यकता है, मुझे संदर्भबद्ध वाक्यविन्यास के बजाय स्पष्ट implicit (एचए) वाक्यविन्यास का उपयोग करना पड़ा। लेकिन स्पष्ट वाक्यविन्यास का उपयोग त्रुटियों के कारण हुई:

scala> case class Foo[+A](a: A)(implicit val m: Manifest[A]) 
<console>:7: error: covariant type A occurs in invariant position in type => Manifest[A] of value m 
     case class Foo[+A](a: A)(implicit val m: Manifest[A]) 
             ^
scala> case class Foo[+A](a: A)(implicit val m: Manifest[_ <: A]) 
defined class Foo 

scala> val foo = Foo(1) 
<console>:9: error: No Manifest available for Int. 

डब्ल्यूटीएफ? संदर्भ बाध्य वाक्यविन्यास कैसे काम करता है और स्पष्ट implicit नहीं करता है? मैंने चारों ओर खोद दिया और समस्या का हल पाया: @uncheckedVariance एनोटेशन।

अद्यतन

मैं कुछ और चारों ओर खोदा और पाया कि स्काला में 2.10 मामले वर्गों के लिए केवलcopy() में पहले पैरामीटर सूची से खेतों की नकल बदल दिया गया है।

मार्टिन का कहना है: केस क्लास नेस केवल पहले तर्क सूची में दिया गया है, बाकी की प्रतिलिपि नहीं बनाई जानी चाहिए।

https://issues.scala-lang.org/browse/SI-5009 पर इस परिवर्तन का विवरण देखें।

+0

मैं ऐसा करने की कोशिश कर रहा था लेकिन 'uncheckedVariance' के बारे में नहीं जानता था। क्या इससे कुछ अवांछित असाइनमेंट हो सकते हैं, या इसके लिए केवल इस तथ्य की एक आर्टिफैक्ट की आवश्यकता है कि अलग-अलग कॉन्वर्सेंट और contravariant 'स्काला में घोषणापत्र' नहीं है? – drhagen

+0

मुझे लगता है कि यह सिर्फ एक आर्टिफैक्ट है। मैं निराश था कि संदर्भ बाध्य वाक्यविन्यास काम करता था, लेकिन स्पष्ट निहित वाक्यविन्यास नहीं था, इसलिए मैंने चारों ओर खोद दिया और 'अनचेक Variance' पाया। मेरा अनुमान है कि संदर्भ बाध्य वाक्यविन्यास मूल रूप से दृश्यों के पीछे 'अनचेक Variance' कर रहा है। – sourcedelica

+0

उत्तर में '@ uncheckedVariance' पर जोड़ा गया जानकारी। – sourcedelica