एक संभव समाधान, कि कुछ उपयोग के मामलों को कवर कर सकते, shapeless
पुस्तकालय से Lens
तों उपयोग करने के लिए है:
import shapeless._
abstract class Foox[T](
implicit l: MkFieldLens.Aux[T, Witness.`'age`.T, Int]
) {
self: T =>
final private val ageLens = lens[T] >> 'age
def age: Int
def addToAge(i: Int): T = ageLens.modify(self)(_ + i)
}
case class Dog(breed: Breed, age: Int) extends Foox[Dog]
case class Person(name: String, age: Int) extends Foox[Person]
ध्यान दें कि एक Lens
आप एक अंतर्निहित MkFieldLens
की जरूरत बनाने के लिए, तो यह आसान है के बजाय के रूप में Foox
को परिभाषित करने के लिए। अन्यथा आपको यह सुनिश्चित करने के लिए हर बच्चे में कुछ कोड लिखना होगा।
इसके अलावा, मुझे नहीं लगता कि प्रत्येक बच्चे में age: Int
को परिभाषित करने से बचने का एक तरीका है। जब आप एक उदाहरण बनाते हैं, तो आपको उम्र को कुछ तरीकों से प्रदान करना होगा, उदा। Dog(Pincher, 5)
, तो आपको उस उम्र के लिए उस कन्स्ट्रक्टर तर्क होना चाहिए।
कुछ और स्पष्टीकरण: एक Haskell Lens tutorial से
उधार:
एक लेंस कुछ डेटा प्रकार के एक subpart के लिए एक प्रथम श्रेणी संदर्भ है। [...] एक लेंस को देखते हुए वहाँ अनिवार्य रूप से तीन बातों subpart
बदल रहा एक और लेंस के साथ इस लेंस कम्बाइन से आप subpart
- देखें करने के लिए पूरे संशोधित करना चाह सकते हैं गहराई से देखने के लिए
पहला और दूसरा इस विचार को जन्म देता है कि लेंस प्राप्तकर्ता और आपके जैसे सेटर्स किसी ऑब्जेक्ट पर हो सकते हैं।
संशोधन भाग का उपयोग age
के साथ हम क्या करना चाहते हैं, इसे लागू करने के लिए किया जा सकता है।
बेकार पुस्तकालय केस वर्ग क्षेत्रों के लिए लेंस को परिभाषित करने और उपयोग करने के लिए एक सुंदर, बॉयलरप्लेट-मुक्त वाक्यविन्यास प्रदान करता है। The code example in the documentation आत्म व्याख्यात्मक है, मुझे विश्वास है।
age
क्षेत्र के लिए निम्न कोड को उस उदाहरण से इस प्रकार है:
final private val ageLens = lens[???] >> 'age
def age: Int
def addToAge(i: Int): ??? = ageLens.modify(self)(_ + i)
addToAge
की वापसी प्रकार क्या होना चाहिए? यह उप-वर्ग का सटीक प्रकार होना चाहिए जिससे इस विधि को बुलाया जा रहा है। यह आमतौर पर F-bounded polymorphism के साथ हासिल किया जाता है। इसलिए हम निम्नलिखित है:
trait Foox[T] { self: T => // variation of F-bounded polymorphism
final private val ageLens = lens[T] >> 'age
def age: Int
def addToAge(i: Int): T = ageLens.modify(self)(_ + i)
}
T
बच्चे का सही प्रकार के रूप में वहाँ प्रयोग किया जाता है, और हर वर्ग Foox[T]
विस्तार (स्वयं प्रकार घोषणा self: T =>
की वजह से) T
के रूप में प्रदान करना चाहिए। उदाहरण के लिए:
case class Dog(/* ... */) extends Foox[Dog]
अब हमें lens[T] >> 'age
लाइन कार्य करने की आवश्यकता है।
की क्या यह कार्य करने के लिए की जरूरत है यह देखने के लिए >>
विधि के हस्ताक्षर का विश्लेषण करते हैं:
def >>(k: Witness)(implicit mkLens: MkFieldLens[A, k.T]): Lens[S, mkLens.Elem]
हम देखते हैं कि 'age
तर्क परोक्ष एक shapeless.Witness
में परिवर्तित हो जाता। Witness
एक विशिष्ट मान के सटीक प्रकार का प्रतिनिधित्व करता है, या दूसरे शब्दों में एक प्रकार-स्तरीय मान का प्रतिनिधित्व करता है। दो अलग-अलग अक्षर, उदा। Symbol
एस 'age
और 'foo
, अलग-अलग गवाह हैं और इस प्रकार उनके प्रकारों को प्रतिष्ठित किया जा सकता है।
बेकार कुछ मूल्य के Witness
प्राप्त करने के लिए एक फैंसी बैकटिक वाक्यविन्यास प्रदान करता है। 'age
प्रतीक के लिए:
Witness.`'age` // Witness object
Witness.`'age`.T // Specific type of the 'age symbol
मद 1 से बाद और >>
हस्ताक्षर, हम, एक अंतर्निहित MkFieldLens
उपलब्ध है की जरूरत वर्ग T
(बच्चे case class
) और क्षेत्र 'age
के लिए:
MkFieldLens[T, Witness.`'age`.T]
age
फ़ील्ड में Int
टाइप होना चाहिए। यह निराकार में साथ Aux
pattern आम इस आवश्यकता को व्यक्त करने के लिए संभव है:
MkFieldLens.Aux[T, Witness.`'age`.T, Int]
और स्वाभाविक रूप से इस अंतर्निहित अधिक प्रदान करने के लिए, एक अंतर्निहित तर्क के रूप में, हम एक trait
के बजाय एक abstract class
उपयोग करना होगा।
ठीक है, मैं इसके बारे में वास्तव में उत्सुक हूं। यह प्रश्न कुछ हद तक समान है (लेकिन इसका डुप्लिकेट नहीं) [http://]] (http://stackoverflow.com/questions/7227641/scala-how-can-i-make-my-immutable-classes-easier-to-subclass) और [यह] (http://stackoverflow.com/questions/21560470/method-inheritance-in-immutable-classes)। क्या किसी को पता है कि क्या दिए गए उत्तर इस प्रश्न के साथ बिल्कुल मदद करते हैं? –
यदि आप एक ही ऑब्जेक्ट की स्थिति को बदलने का प्रयास करते हैं तो आप अपरिवर्तनीय सिद्धांत का उल्लंघन कर रहे हैं। इसलिए 'कॉपी' दृष्टिकोण की तरह लगता है। राज्य को इन ऑब्जेक्ट्स का उपयोग करने वाले कोड द्वारा बनाए रखा जाना चाहिए यानी 'addToAge' – tuxdna
द्वारा लौटाई गई ऑब्जेक्ट का उपयोग करने के लिए अपने राज्य को अपडेट करें, मुझे नहीं लगता कि यहां एक आसान जवाब है। मेरे दिमाग में आने वाला सबसे नज़दीकी विचार लेंस के कुछ रूपों का उपयोग कर रहा है या आकारहीन पुस्तकालय का उपयोग कर रहा है।आपको ओओ छोड़ने और अधिक कार्यात्मक अबास्ट्रक्शन के साथ काम करना होगा। उदाहरण के लिए, यदि आपके पास 'नक्शा()' था जो सिर्फ उम्र को बदल देता है और बाकी सब कुछ छोड़ देता है, तो आप इस मामले में जो चाहते हैं उसे प्राप्त करेंगे। – marios