आइए इस पोस्ट को एक मजेदार अनुमान लगाने वाले गेम के साथ शुरू करें:आपको क्या लगता है कि रूबी अनुप्रयोगों में ऐपसिग्नल द्वारा ट्रैक की जाने वाली सबसे आम त्रुटि क्या है?
यह मान लेना उचित है कि आप में से कई लोगों ने इस प्रश्न का उत्तर NoMethodError
. के साथ दिया है , एक अपवाद जो किसी ऑब्जेक्ट पर एक गैर-मौजूद विधि को कॉल करने के कारण होता है। कभी-कभी, यह विधि के नाम में टाइपो के कारण हो सकता है, लेकिन अधिक बार यह गलत प्रकार के ऑब्जेक्ट पर किसी विधि को कॉल करने का परिणाम होता है, जो अक्सर एक अप्रत्याशित nil
होता है। . क्या ऐसी त्रुटियों की आवृत्ति को कम करने के लिए रूबी डेवलपर्स के रूप में हम कुछ कर सकते हैं?
बचाव के प्रकार?
टेक्स्ट एडिटर या प्रोग्रामिंग भाषा के चुनाव को छोड़कर, कुछ विषय टाइप सिस्टम की चर्चा की तुलना में तेजी से गरमागरम बहस में बदल सकते हैं। हमारे पास यहां विवरण में जाने का समय नहीं होगा, लेकिन क्रिस स्मिथ की पोस्ट "व्हाट टू नो बिफोर डिबेटिंग टाइप सिस्टम्स" उस पर एक उत्कृष्ट काम करती है।
व्यापक शब्दों में, टाइप सिस्टम को दो मुख्य श्रेणियों-स्थिर और गतिशील में विभाजित किया जा सकता है। जबकि पूर्व समय से पहले होता है (या तो संकलक या एक अलग उपकरण के माध्यम से), गतिशील प्रकार की जांच रनटाइम के दौरान होती है, जहां वास्तविक प्रकार डेवलपर की अपेक्षाओं के साथ संरेखित नहीं होने पर अपवाद हो सकते हैं।
दोनों दर्शन के समर्थकों के पास मजबूत राय है, लेकिन अफसोस, कई गलतफहमियां भी तैर रही हैं:स्थिर टाइपिंग के लिए प्रचुर प्रकार के एनोटेशन की आवश्यकता नहीं होती है-कई आधुनिक कंपाइलर अपने आप ही प्रकारों का पता लगा सकते हैं, एक प्रक्रिया जिसे "टाइप इंट्रेंस" के रूप में जाना जाता है। दूसरी ओर, गतिशील रूप से टाइप की गई भाषाएं अपने स्थिर रूप से टाइप किए गए समकक्षों की तुलना में बहुत अधिक दोष दर प्रदर्शित नहीं करती हैं।
बतख टाइपिंग
रूबी स्वयं एक गतिशील रूप से टाइप-चेक की गई भाषा है और "डक टाइपिंग" दृष्टिकोण का अनुसरण करती है:
<ब्लॉकक्वॉट>अगर वह बत्तख की तरह चलता है और बत्तख की तरह झूमता है, तो वह बत्तख ही होगा।
इसका मतलब यह है कि रूबी डेवलपर्स आमतौर पर किसी वस्तु के प्रकार के बारे में ज्यादा चिंता नहीं करते हैं, लेकिन क्या यह कुछ "संदेशों" (या विधियों) का जवाब देता है।
तो रूबी में स्थिर टाइपिंग से परेशान क्यों हैं, आप पूछ सकते हैं? हालांकि यह निश्चित रूप से कोई रामबाण इलाज नहीं है जो आपके कोड को जादुई रूप से बग मुक्त कर देगा, यह कुछ लाभ प्रदान करता है:
- सटीकता:स्थिर टाइपिंग कुछ वर्गों को रोकने में अच्छी है उपरोक्त
NoMethodError
जैसे बगों की संख्या । - टूलिंग:अक्सर, विकास के दौरान स्थिर प्रकार की जानकारी उपलब्ध होने से बेहतर टूलींग विकल्प (जैसे आईडीई में रीफैक्टरिंग समर्थन, आदि) होते हैं
- दस्तावेज़ीकरण:कई सांख्यिकीय रूप से टाइप की गई भाषाओं में महान अंतर्निहित दस्तावेज़ीकरण उपकरण होते हैं। हास्केल का हूगल एक खोज इंजन की पेशकश करके इसका बहुत प्रभाव से उपयोग करता है जहां कार्यों को उनके प्रकार के हस्ताक्षर द्वारा देखा जा सकता है।
- प्रदर्शन:कंपाइलर के लिए जितनी अधिक जानकारी उपलब्ध होगी, उतने अधिक प्रदर्शन अनुकूलन जो संभावित रूप से लागू किए जा सकते हैं।
यह सूची संपूर्ण नहीं है, और इनमें से अधिकांश बिंदुओं के लिए प्रति उदाहरण मिल सकते हैं, लेकिन निश्चित रूप से उनके लिए सच्चाई का एक मूल है।
क्रमिक प्रकार की जांच
हाल के वर्षों में आमतौर पर "क्रमिक प्रकार की जाँच" के रूप में संदर्भित एक दृष्टिकोण ने विभिन्न गतिशील रूप से टाइप-चेक की गई भाषाओं में प्रवेश किया है:जेएस के लिए टाइपस्क्रिप्ट से लेकर PHP के लिए हैक और पायथन के लिए मायपी। इन दृष्टिकोणों में जो समानता है, वह यह है कि उन्हें सभी-या-कुछ दृष्टिकोण की आवश्यकता नहीं होती है, बल्कि इसके बजाय, डेवलपर्स को धीरे-धीरे चर और अभिव्यक्तियों में प्रकार की जानकारी जोड़ने की अनुमति देते हैं जैसा कि वे फिट देखते हैं। यह मौजूदा बड़े कोडबेस के लिए विशेष रूप से उपयोगी है, जहां कोई सिस्टम के सबसे महत्वपूर्ण हिस्सों को स्थिर रूप से जांच सकता है, जबकि बाकी को बिना टाइप किए छोड़ दिया जाता है और रनटाइम पर चेक किया जाता है। रूबी के लिए सभी प्रकार के जाँच समाधान जो हम इस लेख के बाकी हिस्सों में खोजेंगे, उसी दृष्टिकोण का पालन करेंगे।
विकल्प
यह देखने के बाद कि रूबी डेवलपर्स अपने विकास वर्कफ़्लो में स्थिर प्रकार की जाँच क्यों जोड़ना चाहते हैं, ऐसा करने के लिए वर्तमान में कुछ लोकप्रिय विकल्पों का पता लगाने का समय आ गया है। हालांकि, यह ध्यान रखना महत्वपूर्ण है कि रूबी में स्थिर प्रकार की जांच जोड़ने का विचार नया नहीं है। मैरीलैंड विश्वविद्यालय के शोधकर्ताओं ने 2009 की शुरुआत में डायमंडबैक रूबी (ड्रुबी) नामक एक रूबी एक्सटेंशन पर काम किया और टफ्ट्स यूनिवर्सिटी प्रोग्रामिंग लैंग्वेज ग्रुप ने 2013 में द रूबी टाइप चेकर नामक एक पेपर जारी किया, जिसने अंततः आरडीएल प्रोजेक्ट का नेतृत्व किया, जो प्रकार प्रदान करता है। एक पुस्तकालय के रूप में जांच और डिजाइन-दर-अनुबंध क्षमताओं।
शर्बत
स्ट्राइप द्वारा विकसित, सॉर्बेट वर्तमान में रूबी के लिए सबसे चर्चित टाइप चेकिंग समाधान है, कम से कम इसलिए नहीं कि Shopify, GitLab, किकस्टार्टर और कॉइनबेस जैसी बड़ी कंपनियां इसके बंद बीटा चरण के दौरान शुरुआती अपनाने वाली थीं। इसकी मूल रूप से पिछले साल की रूबी कैगी के दौरान घोषणा की गई थी और इस साल 20 जून को इसकी पहली सार्वजनिक रिलीज देखी गई थी। सॉरबेट आधुनिक सी ++ में लिखा गया है और मैटज़ की प्राथमिकताओं के बावजूद (उद्धरण:"आई हेट टाइप एनोटेशन"), टाइप एनोटेशन के आधार पर एक दृष्टिकोण का चयन किया। सॉर्बेट के बारे में एक विशेष रूप से दिलचस्प बात यह है कि यह स्थिर और गतिशील प्रकार की जाँच के संयोजन का विकल्प चुनता है क्योंकि रूबी की अत्यंत गतिशील प्रकृति और मेटाप्रोग्रामिंग क्षमताएं स्थिर प्रकार के सिस्टम के लिए चुनौतीपूर्ण हैं।
# typed: true
class Test
extend T::Sig
sig {params(x: Integer).returns(String)}
def to_s(x)
x.to_s
end
end
टाइप चेकिंग को सक्षम करने के लिए, हमें सबसे पहले # typed: true
. जोड़ना होगा जादुई टिप्पणी करें और T::Sig
. के साथ हमारी कक्षा का विस्तार करें मापांक। वास्तविक प्रकार का एनोटेशन sig
. के साथ निर्दिष्ट किया गया है विधि:
sig {params(x: Integer).returns(String)}
जो निर्दिष्ट करता है कि यह विधि x
. नामक एक एकल तर्क लेती है यह Integer
. प्रकार का है और एक String
देता है . इस विधि को गलत तर्क प्रकार के साथ कॉल करने का प्रयास करने से त्रुटि होगी:
Test.new.to_s("42")
# Expected Integer but found String("42") for argument x
इन बुनियादी जाँचों के अलावा, शर्बत की आस्तीन में कुछ और तरकीबें हैं। उदाहरण के लिए, यह हमें खतरनाक NoMethodError
. से बचा सकता है पर nil
:
users = T::Array[User].new
user = users.first
user.username
# Method username does not exist on NilClass component of T.nilable(User)
ऊपर दिया गया स्निपेट User
. की एक खाली सरणी को परिभाषित करता है ऑब्जेक्ट और जब हम पहले तत्व तक पहुंचने का प्रयास करते हैं (जो nil
. लौटाएगा ) शर्बत हमें सही चेतावनी देता है कि username
. नाम की कोई विधि नहीं है NilClass
. पर उपलब्ध है . हालांकि, अगर हमें यकीन है कि एक निश्चित मान कभी नहीं हो सकता nil
, हम T.must
. का उपयोग कर सकते हैं शर्बत को यह बताने के लिए:
users = T::Array[User].new
user = T.must(users.first)
user.username
जबकि उपरोक्त कोड अब चेक टाइप करेगा, इससे रनटाइम अपवाद हो सकता है, इसलिए इस सुविधा का सावधानी से उपयोग करें।
शर्बत हमारे लिए और भी बहुत कुछ कर सकता है:डेड कोड डिटेक्शन, टाइप पिनिंग (अनिवार्य रूप से एक निश्चित प्रकार के लिए एक वैरिएबल बनाना, उदाहरण के लिए, एक बार इसे एक स्ट्रिंग असाइन करने के बाद, इसे कभी भी पूर्णांक नहीं दिया जा सकता है), या क्षमता इंटरफेस को परिभाषित करने के लिए।
इसके अतिरिक्त, शर्बत "रूबी इंटरफेस" फाइलों के साथ भी काम कर सकता है (rbi
) जिसे वह sorbet/
. में रखता है आपकी वर्तमान कार्यशील निर्देशिका में फ़ोल्डर। यह हमें प्रोजेक्ट द्वारा उपयोग किए जाने वाले सभी रत्नों के लिए इंटरफ़ेस परिभाषाएँ उत्पन्न करने की अनुमति देता है, जो हमें और भी अधिक प्रकार की त्रुटियों को खोजने में मदद कर सकता है।
सॉर्बेट में हम एक ही लेख में कवर कर सकते हैं (उदाहरण के लिए अलग-अलग सख्ती के स्तर या मेटाप्रोग्रामिंग प्लगइन्स), लेकिन इसका दस्तावेज पहले से ही बहुत अच्छा है और पीआर के लिए खुला है।
खड़ी
सॉर्बेट का सबसे व्यापक रूप से ज्ञात विकल्प साउथारो मात्सुमोतो द्वारा खड़ी है। यह एनोटेशन का उपयोग नहीं करता है और किसी भी प्रकार का अनुमान अपने आप नहीं करता है। इसके बजाय, यह पूरी तरह से .rbi
. पर निर्भर करता है sig
. में फ़ाइलें निर्देशिका।
आइए निम्नलिखित सरल रूबी वर्ग से शुरू करें:
class User
attr_reader :first_name, :last_name, :address
def initialize(first_name, last_name, address)
@first_name = first_name
@last_name = last_name
@address = address
end
def full_name
"#{first_name} #{last_name}"
end
end
अब हम एक प्रारंभिक user.rbi
बना सकते हैं निम्नलिखित कमांड के साथ फाइल करें:
$ steep scaffold user.rb > sig/user.rbi
इसका परिणाम निम्न फ़ाइल में होता है जो एक प्रारंभिक बिंदु के रूप में अभिप्रेत है (इस तथ्य से सचित्र है कि सभी प्रकारों को any
के रूप में निर्दिष्ट किया गया है , जो कोई सुरक्षा प्रदान नहीं करता है):
class User
@first_name: any
@last_name: any
@address: any
def initialize: (any, any, any) -> any
def full_name: () -> String
end
हालांकि, अगर हम इस बिंदु पर चेक टाइप करने का प्रयास करते हैं, तो हम कुछ त्रुटियों का सामना करेंगे:
$ steep check
user.rb:11:7: NoMethodError: type=::User, method=first_name (first_name)
user.rb:11:21: NoMethodError: type=::User, method=last_name (last_name)
हम इन्हें देख रहे हैं इसका कारण यह है कि स्टीप को यह जानने के लिए एक विशेष टिप्पणी की आवश्यकता है कि attr_reader
के माध्यम से किन विधियों को परिभाषित किया गया है। s, तो चलिए इसे जोड़ते हैं:
# @dynamic first_name, last_name, address
attr_reader :first_name, :last_name, :address
इसके अतिरिक्त, हमें जेनरेट किए गए .rbi
. में विधियों के लिए परिभाषाएं जोड़ने की आवश्यकता है फ़ाइल। जब तक हम इस पर हैं, हम any
. से हस्ताक्षर भी बदलते हैं वास्तविक प्रकारों के लिए:
class User
@first_name: String
@last_name: String
@address: Address
def initialize: (String, String, Address) -> any
def first_name: () -> String
def last_name: () -> String
def address: () -> Address
def full_name: () -> String
end
अब, सब कुछ अपेक्षा के अनुरूप काम करता है और steep check
कोई त्रुटि नहीं लौटाता है।
अब तक हमने जो देखा है, उसके ऊपर, स्टीप जेनरिक का भी समर्थन करता है (जैसे Hash<Symbol, String>
) और संघ प्रकार, जो कई प्रकारों के बीच या तो या पसंद का प्रतिनिधित्व करते हैं। उदाहरण के लिए, किसी उपयोगकर्ता का top_post
विधि उपयोगकर्ता द्वारा लिखी गई उच्चतम-रैंक वाली पोस्ट लौटा सकती है, या nil
अगर उन्होंने अभी तक कुछ भी योगदान नहीं दिया है। इसे संघ प्रकार (Post | nil)
. के माध्यम से दर्शाया जाता है , और संबंधित हस्ताक्षर इस तरह दिखाई देंगे:
def top_post: () -> (Post | nil)
जबकि स्टीप में शर्बत की तुलना में निश्चित रूप से कम विशेषताएं हैं, यह अभी भी एक सहायक उपकरण है और ऐसा लगता है कि माट्ज़ ने रूबी 3 में टाइप चेकिंग की तरह दिखने की कल्पना की थी।
रूबी टाइप प्रोफाइलर
कुकपैड से युसुके एंडोह (रूबी डेवलपर सर्कल में "मैम" के रूप में जाना जाता है) तथाकथित स्तर 1 प्रकार के चेकर पर काम कर रहा है जिसे रूबी टाइप प्रोफाइलर कहा जाता है। यहां प्रस्तुत अन्य समाधानों के विपरीत, इसे हस्ताक्षर फ़ाइलों या टाइप एनोटेशन की आवश्यकता नहीं है, बल्कि इसे पार्स करते समय रूबी प्रोग्राम के बारे में जितना संभव हो उतना अनुमान लगाने की कोशिश करता है। हालांकि यह खड़ी या शर्बत की तुलना में बहुत कम संभावित समस्याओं को पकड़ता है, यह डेवलपर के लिए बिना किसी अतिरिक्त लागत के आता है।
सारांश
जबकि कोई भी भविष्य की भविष्यवाणी नहीं कर सकता है, ऐसा लगता है कि रूबी में टाइप चेकिंग कुछ ऐसा है जो यहां रहने के लिए है। वर्तमान में, .rbi
में उपयोग के लिए "रूबी सिग्नेचर लैंग्वेज" के मानकीकरण के प्रयास चल रहे हैं। फाइलें (संभावित रूप से रूबी टाइप प्रोफाइलर द्वारा मचान), इसलिए डेवलपर्स जो भी उपकरण पसंद करते हैं उसका उपयोग कर सकते हैं। खड़ी पहले से ही पुस्तकालय लेखकों को अपने रत्नों के साथ प्रकार की जानकारी भेजने की अनुमति देती है, और सॉर्बेट में शर्बत-टाइप के रूप में एक समान तंत्र है, जो टाइपस्क्रिप्ट परिभाषाओं के लिए निश्चित रूप से टाइप किए गए भंडार से प्रेरित था। यदि आप रूबी में टाइप चेकिंग के भविष्य को आकार देने में मदद करने में रुचि रखते हैं, तो अब इसमें शामिल होने का एक अच्छा समय है!