मुझे नहीं पता कि इस समस्या के लिए कोई विशेष शब्द है, लेकिन समाधान के तीन सामान्य वर्ग हैं:
- गतिशील प्रेषण के पक्ष में ठोस प्रकारों से बचें
- प्लेसहोल्डर प्रकार के मापदंडों को टाइप बाधाओं में अनुमति दें
- संबद्ध प्रकार / प्रकार के परिवारों का उपयोग करके प्रकार के मापदंडों से बचें
और निश्चित रूप से डिफ़ॉल्ट समाधान: उन सभी मापदंडों की वर्तनी रखें।
ठोस प्रकार से बचें।
आपने एक Iterable
इंटरफ़ेस परिभाषित किया है:
interface <Element> Iterable<T: Iterator<Element>> {
getIterator(): T
}
यह इंटरफ़ेस के उपयोगकर्ताओं को अधिकतम शक्ति देता है क्योंकि उन्हें इट्रेटर का सटीक ठोस प्रकार मिलता T
है। यह संकलक को अधिक अनुकूलन जैसे कि इनलाइनिंग लागू करने की अनुमति देता है।
हालांकि, यदि Iterator<E>
एक गतिशील रूप से भेजा गया इंटरफ़ेस है, तो कंक्रीट प्रकार जानना आवश्यक नहीं है। यह उदाहरण है कि जावा द्वारा उपयोग किया जाने वाला समाधान। इंटरफ़ेस तब लिखा जाएगा:
interface Iterable<Element> {
getIterator(): Iterator<Element>
}
इस का एक दिलचस्प बदलाव है रस्ट impl Trait
वाक्यविन्यास जो आपको फ़ंक्शन को अमूर्त रिटर्न प्रकार के साथ घोषित करता है, लेकिन यह जानते हुए कि कंक्रीट प्रकार कॉल साइट पर जाना जाएगा (इस प्रकार अनुकूलन की अनुमति देता है)। यह एक अंतर्निहित प्रकार के पैरामीटर के समान व्यवहार करता है।
प्लेसहोल्डर टाइप पैरामीटर की अनुमति दें।
Iterable
इंटरफेस, तत्व प्रकार के बारे में पता करने के लिए तो यह इस रूप में लिखने के लिए संभव हो सकता है की जरूरत नहीं है:
interface Iterable<T: Iterator<_>> {
getIterator(): T
}
जहां T: Iterator<_>
तत्व प्रकार की परवाह किए बिना बाधा "टी किसी भी पुनरावृत्त है," व्यक्त करता है। अधिक कठोरता से, हम इसे इस रूप में व्यक्त कर सकते हैं: "कुछ प्रकार मौजूद है Element
इसलिए T
यह एक है Iterator<Element>
", बिना किसी ठोस प्रकार को जाने Element
। इसका मतलब यह है कि टाइप-एक्सप्रेशन Iterator<_>
एक वास्तविक प्रकार का वर्णन नहीं करता है, और केवल एक प्रकार की बाधा के रूप में इस्तेमाल किया जा सकता है।
टाइप परिवारों / संबंधित प्रकारों का उपयोग करें।
जैसे C ++ में, एक प्रकार के सदस्य हो सकते हैं। यह आमतौर पर पूरे मानक पुस्तकालय में उपयोग किया जाता है, उदाहरण के लिए std::vector::value_type
। यह वास्तव में सभी परिदृश्यों में टाइप पैरामीटर समस्या को हल नहीं करता है, लेकिन चूंकि एक प्रकार अन्य प्रकारों को संदर्भित कर सकता है, इसलिए एक एकल प्रकार का पैरामीटर संबंधित प्रकारों के पूरे परिवार का वर्णन कर सकता है।
आइए परिभाषित करते हैं:
interface Iterator {
type ElementType
fn next(): ElementType
}
interface Iterable {
type IteratorType: Iterator
fn getIterator(): IteratorType
}
फिर:
class Vec<Element> implement Iterable {
type IteratorType = VecIterator<Element>
fn getIterator(): IteratorType { ... }
}
class VecIterator<T> implements Iterator {
type ElementType = T
fn next(): ElementType { ... }
}
यह बहुत लचीला दिखता है, लेकिन ध्यान दें कि इससे प्रकार की बाधाओं को व्यक्त करना अधिक कठिन हो सकता है। उदाहरण के लिए, लिखित रूप में Iterable
किसी भी पुनरावृत्ति तत्व प्रकार को लागू नहीं किया जाता है , और हम interface Iterator<T>
इसके बजाय घोषित करना चाहते हैं । और अब आप काफी जटिल प्रकार के पथरी से निपट रहे हैं। गलती से इस तरह की प्रणाली को अनिर्दिष्ट बनाना आसान है (या शायद यह पहले से ही है?)।
ध्यान दें कि संबंधित प्रकार, प्रकार के मापदंडों के लिए चूक के रूप में बहुत सुविधाजनक हो सकते हैं। उदाहरण के लिए, यह मानते हुए कि Iterable
इंटरफ़ेस को तत्व प्रकार के लिए एक अलग प्रकार के पैरामीटर की आवश्यकता होती है जो आमतौर पर है, लेकिन हमेशा इट्रेटर तत्व प्रकार के समान नहीं है, और हमारे पास प्लेसहोल्डर प्रकार के पैरामीटर हैं, यह कहना संभव हो सकता है:
interface Iterable<T: Iterator<_>, Element = T::Element> {
...
}
हालाँकि, यह केवल एक भाषा एर्गोनॉमिक्स सुविधा है, और यह भाषा को अधिक शक्तिशाली नहीं बनाती है।
टाइप सिस्टम मुश्किल है, इसलिए अन्य भाषाओं में क्या काम करता है और क्या नहीं, इस पर एक नज़र रखना अच्छा है।
उदाहरण के लिए उन्नत लक्षण पढ़ने पर विचार करें । रस्ट बुक में चैप्टर को करें, जो संबंधित प्रकारों पर चर्चा करता है। लेकिन ध्यान दें कि जेनरिक के बजाय संबद्ध प्रकारों के पक्ष में कुछ बिंदु केवल वहां लागू होते हैं क्योंकि भाषा में उप-योग की सुविधा नहीं होती है और प्रत्येक लक्षण को केवल एक बार प्रत्येक प्रकार पर लागू किया जा सकता है। यानी जंग के लक्षण जावा जैसे इंटरफेस नहीं हैं।
अन्य दिलचस्प प्रकार की प्रणालियों में विभिन्न भाषा एक्सटेंशन के साथ हास्केल शामिल हैं। OCaml मॉड्यूल / फंक्शनलर्स टाइप परिवारों के एक तुलनात्मक रूप से सादे संस्करण हैं, वस्तुओं या पैरामीटर प्रकारों के साथ सीधे हस्तक्षेप के बिना। जावा अपने प्रकार प्रणाली में सीमाओं के लिए उल्लेखनीय है, उदाहरण के लिए erasure के साथ जेनरिक, और मूल्य प्रकारों पर कोई जेनरिक नहीं। C # बहुत जावा की तरह है, लेकिन कार्यान्वयन की जटिलता की कीमत पर, इन सीमाओं से अधिकांश से बचने का प्रबंधन करता है। स्कैला जावा प्लेटफॉर्म के शीर्ष पर हास्केल-शैली के टाइपसेकल्स के साथ सी # -स्टाइल जेनरिक को एकीकृत करने की कोशिश करता है। C ++ के भ्रामक सरल टेम्प्लेट अच्छी तरह से अध्ययन किए जाते हैं, लेकिन अधिकांश जेनरिक कार्यान्वयन के विपरीत होते हैं।
यह इन भाषाओं के मानक पुस्तकालयों (विशेष रूप से मानक पुस्तकालय संग्रह जैसे सूचियों या हैश टेबल) को देखने के लायक है, यह देखने के लिए कि कौन से पैटर्न आमतौर पर उपयोग किए जाते हैं। ईजी सी ++ में विभिन्न पुनरावृत्त क्षमताओं की एक जटिल प्रणाली है, और स्काला ने लक्षण के रूप में ठीक-ठीक संग्रह क्षमताओं को एन्कोड किया है। जावा मानक लाइब्रेरी इंटरफेस कभी-कभी अनसोल्ड होते हैं, जैसे Iterator#remove()
, लेकिन नेस्टेड क्लासेस को एक प्रकार के संबद्ध प्रकार (जैसे Map.Entry
) के रूप में उपयोग कर सकते हैं ।