हाल ही में मोजो में विशेषताएं पेश की गईं, इसलिए मैंने सोचा कि मैं उन्हें आज़माऊंगा। वर्तमान में, अंतर्निहित लक्षणों में CollectionElement , Copyable , Destructable , Intable , Movable , Sized , और Stringable शामिल हैं (ऐसा लगता है कि इन नामकरण परंपराओं में "-able" प्रत्यय एक चीज़ है!)।
लक्षण संरचनाओं पर काम करते हैं। किसी विशेषता को लागू करने के लिए, आप बस संरचना में एक विधि जोड़ते हैं जो विशेषता से मेल खाती है; फिर विशेषता नाम को पैरामीटर के रूप में पास करें:
@value struct Duck(Quackable): fn quack(self): print("Quack!")
मोजो में @value
डेकोरेटर __init__()
, __copyinit__()
और __moveinit__()
जैसी जीवनचक्र विधियों को संरचना में सम्मिलित करता है, जिससे हमारा जीवन थोड़ा सरल हो जाता है क्योंकि हमें उन्हें स्वयं जोड़ने की आवश्यकता नहीं होती है।
मोजो में लक्षण अभी तक विधियों के लिए डिफ़ॉल्ट कार्यान्वयन का समर्थन नहीं करते हैं, इसलिए, उपरोक्त Quackable
विधि के मुख्य भाग में ...
आप pass
भी उपयोग कर सकते हैं, जिसका प्रभाव Python जैसा ही होगा।
अलग-अलग नाम के बावजूद, मोजो में लक्षणों के लिए बुनियादी दृष्टिकोण मुझे गो इंटरफेस की याद दिलाता है। गो में, आप एक Duck
संरचना को परिभाषित करेंगे और इस तरह एक Quackable
इंटरफ़ेस लागू करेंगे:
type Quackable interface { quack() }
और एक ऐसी संरचना बनाने के लिए जो इस इंटरफ़ेस को संतुष्ट करती हो:
type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") }
इसकी तुलना मोजो कार्यान्वयन से करें:
trait Quackable: fn quack(self): ... @value struct Duck(Quackable): fn quack(self): print("Quack!")
मुझे लगता है कि मोजो संस्करण और भी अधिक संक्षिप्त है, जो Duck
संरचना प्रकार के अंदर quack()
विधि परिभाषा को समाहित करता है। संरचनाओं पर काम करने के अलावा, मोजो में लक्षणों के लिए गो की तरह ही implements
कीवर्ड की आवश्यकता नहीं होती है।
दिलचस्प बात यह है कि मोजो डॉक्स में यह बात कही गई है कि डिफ़ॉल्ट कार्यान्वयन की अभी अनुमति नहीं है, जिसका अर्थ है कि भविष्य में उन्हें अनुमति दी जा सकती है। इसका मतलब यह है कि मोजो गो से अलग दृष्टिकोण अपना सकता है, जो इंटरफ़ेस को संतुष्ट करने वाली संरचनाओं पर ध्यान केंद्रित करता है, न कि इसे लागू करने पर ।
गो में डिफ़ॉल्ट विधि कार्यान्वयन की आवश्यकता नहीं है, और एक समान प्रभाव एम्बेडिंग के साथ प्राप्त किया जाता है, क्योंकि इंटरफ़ेस/विशेषता कार्यान्वयन केवल एक अवधारणा है जो गो में मौजूद नहीं है। मोजो में चीज़ें भिन्न हो सकती हैं।
लक्षण और इंटरफ़ेस का महत्व कोड को पुन: प्रयोज्य बनाना है। उदाहरण के लिए, मोजो में, आप ऐसे फ़ंक्शन लिख सकते हैं जो विशेषता प्रकारों को स्वीकार करते हैं...
fn make_it_quack[T: Quackable](could_be_duck: T): could_be_duck.quack()
और फिर अलग-अलग संरचनाएं पास करें जो इनपुट के समान गुण को लागू करती हैं - सब कुछ बस काम करेगा! उदाहरण के लिए, यहां एक Goose
संरचना है जो Quackable
लागू करती है:
@value struct Goose(Quackable): fn quack(self): print("Honk!")
और यहां, Goose
और Duck
दोनों पर make_it_quack
कॉल करने के लिए (याद रखें, आपको अपने प्रोग्राम में प्रवेश बिंदु के रूप में मोजो में main
फ़ंक्शन की आवश्यकता है):
def main(): make_it_quack(Duck()) make_it_quack(Goose())
इसका आउटपुट होगा
Quack! Honk!
यदि मैंने कुछ ऐसा पारित करने का प्रयास किया है जो make_it_quack
फ़ंक्शन में Quackable
विशेषता को लागू नहीं करता है, मान लें कि StealthCow
, प्रोग्राम संकलित नहीं होगा:
@value struct StealthCow(): pass
make_it_quack(StealthCow())
नीचे दिया गया त्रुटि संदेश अधिक वर्णनात्मक हो सकता था; शायद मोजो टीम इसमें सुधार करेगी?
error: invalid call to 'make_it_quack': callee expects 1 input parameter, but 0 were specified
यदि मैं Goose
से quack
विधि हटा दूं तो भी ऐसा ही होगा; यहाँ, हमें एक अच्छी, वर्णनात्मक त्रुटि मिलती है:
struct 'Goose' does not implement all requirements for 'Quackable' required function 'quack' is not implemented
शुद्ध पायथन दृष्टिकोण की तुलना में इस प्रकार की स्थैतिक प्रकार की जाँच का लाभ यह है कि हम त्रुटियों को आसानी से पकड़ सकते हैं और किसी भी गलती को उत्पादन में भेजने से बच सकते हैं क्योंकि कोड आसानी से संकलित नहीं होगा।
मोजो में लक्षण पहले से ही वंशानुक्रम का समर्थन करते हैं, इसलिए हमारा `क्वैकएबल` गुण `श्रव्य` गुण का विस्तार इस प्रकार कर सकता है:
trait Audible: fn make_sound(self): ...
trait Quackable(Audible): fn quack(self): ...
इसका मतलब यह है कि Duck
संरचना को Quackable
विशेषता के अनुरूप होने के लिए quack
और make_sound
दोनों को लागू करना होगा।
यह गो में "इंटरफ़ेस एम्बेडिंग" की अवधारणा के समान है, जहां एक नया इंटरफ़ेस बनाने के लिए जो अन्य इंटरफ़ेस से प्राप्त होता है, आपको इस तरह से मूल इंटरफ़ेस एम्बेड करना होगा:
type Quackable interface { Audible // includes methods of Audible in Quackable's method set }
लक्षण स्थिर तरीकों को भी स्वीकार करते हैं जो किसी संरचना का उदाहरण बनाए बिना काम करते हैं:
trait Swims: @staticmethod fn swim(): ... @value struct Duck(Quackable, Swims): fn quack(self): print("Quack!") @staticmethod fn swim(): print("Swimming")
fn make_it_swim[T: Swims](): T.swim()
आप इस प्रकार एक स्थिर विधि को कॉल करते हैं:
def main(): make_it_quack(Duck()) make_it_quack(Goose()) Duck.swim()
जो आउटपुट देगा:
Quack! Honk! Swimming
ध्यान दें कि अंतिम विधि कॉल में, डक इंस्टेंस नहीं बनाया गया है। ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग से कुछ हद तक हटकर, पायथन में स्थिर विधियाँ इस प्रकार काम करती हैं। मोजो इस पायथन कार्यक्षमता पर आधारित है।
दिलचस्प बात यह है कि एक खाली interface{}
जो किसी भी प्रकार को पास करने की अनुमति देती है और गो जेनेरिक्स पेश किए जाने से पहले गो समुदाय के साथ लोकप्रिय थी, मोजो-टाइप किए गए फ़ंक्शन fn
के साथ काम नहीं करेगी।
आपकी संरचना को __len__
या __str__
जैसी जीवनचक्र विधियों में से कम से कम एक को लागू करना होगा, जो इस मामले में, इसे Sized
या Stringable
के अनुरूप बनाएगा, जिसका उपयोग उन कार्यों के साथ किया जाएगा जो विशेषता प्रकारों को स्वीकार करते हैं।
यह वास्तव में कोई वास्तविक सीमा नहीं है और मोजो में बहुत मायने रखता है, क्योंकि, मोजो के साथ, यदि आपको गतिशील टाइपिंग की आवश्यकता है, तो आप अधिक लचीले def
फ़ंक्शन पर वापस आ सकते हैं, और फिर अज्ञात के साथ काम करने के लिए सामान्य पायथन जादू लागू कर सकते हैं प्रकार.
अधिक सख्त मोजो fn
फ़ंक्शन DynamicVector
जैसे प्रकारों का उपयोग करके सामान्य संरचनाओं पर भी काम करते हैं; उसके बारे में और अधिक यहां पढ़ें ।
एक और सीमा जो मैं देखता हूं वह लक्षणों के बजाय संरचनाओं और उनकी विधियों से संबंधित है और स्वच्छ वास्तुकला को लागू करने/कोड को विभिन्न भागों में अलग करने में एक संभावित बाधा है।
गो बनाम मोजो संरचना विधि परिभाषा के साथ पिछले उदाहरणों में से एक पर विचार करें:
type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") }
@value struct Duck(Quackable): fn quack(self): print("Quack!")
मोजो उदाहरण, अधिक पायथन-जैसे वाक्यविन्यास का पालन करते हुए, विधि परिभाषा को सीधे संरचना के अंदर रखता है, जबकि गो संस्करण इसे संरचना से अलग करने की अनुमति देता है। इस संस्करण में, यदि मेरे पास कई प्रकारों और विधियों के साथ एक बहुत लंबी संरचना है, तो इसे पढ़ना कुछ हद तक कठिन होगा।
हालाँकि यह कोई गंभीर अंतर नहीं है, बस जागरूक होने की बात है।
भाषा के शुरुआती दौर में होने के बावजूद मोजो के लक्षण पहले से ही काफी उपयोगी हैं। जबकि गो दर्शन पूरी तरह से सरलता के बारे में है और सुविधाओं को न्यूनतम रखने की कोशिश करता है, यह संभावना है कि हम भविष्य में मोजो विशेषताओं में और अधिक कार्यक्षमता जोड़ेंगे, जिससे वे और भी अधिक शक्तिशाली बन जाएंगे और कई अलग-अलग उपयोग के मामलों को सक्षम कर पाएंगे।