Crystal में एक वेरिएबल कई टाइप का हो सकता है। इसे यूनियन टाइप कहते हैं। Crystal में यूनियन टाइप का अनुमान अक्सर कंपाइलर खुद लगा लेता है।
रनटाइम पर यूनियन टाइप एक ही टाइप होता है, भले ही वह कई टाइपों से बना हो।
इसका मतलब है कि अगर कोई यूनियन टाइप String और Int32 से बना है, तो वह एक ही समय में दोनों नहीं होगा।
वह या तो String होगा या Int32।
यूनियन टाइप घोषित करने के लिए टाइपों को पाइप (|) से अलग किया जाता है।
इन्हें अक्सर गोल ब्रैकेट में रखा जाता है, लेकिन यह ज़रूरी नहीं है।
सबसे आम यूनियन टाइप (T | Nil) है, जिसमें T कोई टाइप है।
ऐसा यूनियन टाइप उन मेथड में मिलता है जो Nil लौटा सकते हैं।
इसे यह भी कहा जाता है कि वेरिएबल निलेबल है।
foo : String = "Hello"
foo = nil # Error: type must be String, not (String | Nil)
foo : (String | Nil) = "Hello"
foo = nil
यह सिर्फ दो टाइपों तक सीमित नहीं है, बल्कि जितने चाहें उतने टाइप रख सकते हैं।
foo : (String | Int32 | Nil | Float64) = "Hello"
foo = 1
foo = nil
foo = 1.0
typeof vs Object#class
किसी वेरिएबल का टाइप पता करने के दो तरीके हैं।
या तो typeof से, या Object#class से।
अंतर यह है कि typeof कंपाइल टाइम पर वेरिएबल का टाइप देता है, जबकि Object#class रनटाइम पर टाइप देता है।
इसका मतलब है कि अगर आप देखना चाहते हैं कि कोई वेरिएबल यूनियन टाइप है या नहीं, तो Object#class आपको यह नहीं बता पाएगा, क्योंकि वह रनटाइम पर ही टाइप देता है, और वहाँ टाइप एक ही होता है।
foo = 0 == 0 ? "a" : 1
typeof(foo) # => (String | Int32)
foo.class # => String
चूँकि रनटाइम पर यूनियन टाइप एक ही टाइप होता है, इसलिए उस पर सारे सामान्य ऑपरेशन काम करते हैं। लेकिन कोड कंपाइल करते समय कंपाइलर को यह जानना होता है कि वह कौन-सा टाइप है। इसलिए जब टाइप-विशेष ऑपरेशन इस्तेमाल करने हों, तो कोड ऐसे लिखा जाना चाहिए कि वह उनमें से सिर्फ एक टाइप ही हो सके।
foo : (String | Int32) = "Hello"
foo.downcase # Error: undefined method 'downcase' for (String | Int32)
Crystal में यूनियन टाइप के लिए एक खास मेथड है: is_a? मेथड, जो आर्गुमेंट के रूप में एक टाइप लेता है और बूलियन लौटाता है।
nil? मेथड is_a?(Nil) का छोटा रूप है।
is_a? मेथड को किसी कंट्रोल एक्सप्रेशन में रखने से कंपाइलर को पता चल जाता है कि वह कौन-सा टाइप है, और इस तरह यह गारंटी हो जाती है कि वह वही टाइप है।
और else ब्रांच में यह गारंटी होती है कि वह उस टाइप का नहीं है।
foo : (String | Int32) = "Hello"
if foo.is_a?(String)
typeof(foo) # => String
foo.downcase # => "hello"
end
यह is_a? सिर्फ एक टाइप को आर्गुमेंट लेने तक सीमित नहीं है, बल्कि यूनियन टाइप भी ले सकता है।
कई टाइपों के लिए इसे && के साथ भी जोड़ा जा सकता है।
is_a? मेथड, जब इसे किसी कंट्रोल एक्सप्रेशन के साथ इस्तेमाल किया जाता है, तो वह इंस्टेंस वेरिएबल या क्लास वेरिएबल नहीं हो सकता।
इसके लिए पहले उन्हें किसी लोकल वेरिएबल में असाइन करना पड़ता है।
यूनियन टाइप को एक ही टाइप बनाने का एक तरीका यह है कि ब्रांच में तभी प्रवेश हो जब टाइप कोई खास टाइप हो।
दूसरा तरीका as मेथड इस्तेमाल करना है।
यह रनटाइम पर जाँच करके यूनियन टाइप को एक ही टाइप बना देता है।
अगर टाइप वह नहीं है जिसकी उम्मीद है, तो एक एक्सेप्शन उठाया जाता है।
foo : String | Int32 = "Hello"
foo.as(String).downcase # => "hello"
foo.as(Int32) # Error: can't cast String to Int32
यह तरीका सिर्फ तभी इस्तेमाल कीजिए जब आपको पक्का हो कि टाइप वही है जिसकी उम्मीद है, या जब आप ऐसा न होने पर एक्सेप्शन उठाना चाहते हों।
गलत तरीके से सेटअप किए गए कोड के साथ यह तरीका इस्तेमाल करने पर अनपेक्षित व्यवहार हो सकता है।
as? लगभग as की तरह ही काम करता है, लेकिन अगर टाइप वह नहीं है जिसकी उम्मीद है, तो एक्सेप्शन उठाने के बजाय यह nil लौटाता है।
इसका मतलब है कि यह अपेक्षित टाइप और Nil का यूनियन टाइप लौटाता है।
foo : (String | Int32) = "Hello"
foo.as?(String).downcase # => "hello"
foo.as?(Int32) # => nil
निलेबल का मतलब है कि वेरिएबल या तो कोई टाइप हो सकता है या Nil।
इसे (T | Nil) लिखा जा सकता है।
लेकिन चूँकि निलेबल टाइप काफी आम हैं, इसलिए इनके लिए एक छोटा रूप भी है: T?।
# This:
foo : (String | Nil) = "Hello"
foo = nil
# Is the same as:
foo : String? = "Hello"
foo = nil
जिस कंपनी के लिए आप काम करते हैं, वह अपना बिल्कुल नया स्मार्टफोन लॉन्च करने वाली है, जिसका नाम Smarty 5 है। इसमें बिल्कुल नया कैमरा सिस्टम, नया डिज़ाइन और एक क्रांतिकारी नया प्रोसेसर है, जो smartyOS नाम के नए ऑपरेटिंग सिस्टम पर चलता है।
नया प्रोसेसर पहले से कहीं अधिक सुरक्षित पासवर्ड संभाल सकता है, और कंपनी ने इसका फायदा उठाने का फैसला किया है। उन्होंने आपसे फोन के लिए एक पासवर्ड लॉक सिस्टम बनाने को कहा है, जिससे उपयोगकर्ता पासवर्ड सेट कर सके और फिर जाँच सके कि दिया गया पासवर्ड सही है या नहीं।
इस अभ्यास में जिस पासवर्ड सिस्टम का अभ्यास किया जाता है, वह सुरक्षित नहीं है और इसका उपयोग केवल शैक्षिक उद्देश्यों के लिए किया जाता है। इसका उपयोग किसी भी वास्तविक ऐप्लिकेशन में नहीं करना चाहिए।
यह फोन उपयोगकर्ताओं को कई अलग-अलग तरह के पासवर्ड इस्तेमाल करने देता है, एक साधारण अंकों वाले पासवर्ड से लेकर अधिक सुरक्षित अल्फान्यूमेरिक पासवर्ड तक, और यहाँ तक कि उनकी फिंगरप्रिंट संग्रहीत करने वाला पासवर्ड भी।
ये अलग-अलग तरह के पासवर्ड अलग-अलग डेटा टाइप इस्तेमाल करते हैं: अंकों वाला पासवर्ड Int32 इस्तेमाल करता है, अल्फान्यूमेरिक पासवर्ड String इस्तेमाल करता है, और फिंगरप्रिंट पासवर्ड Float64 इस्तेमाल करता है।
क्लास PasswordLock को एक इनिशियलाइज़र के साथ बनाइए, जो तीनों तरह के पासवर्ड में से कोई भी आर्गुमेंट के रूप में लेता है और उसे @password नाम के इंस्टेंस वेरिएबल में संग्रहीत करता है।
password_lock = PasswordLock.new(1234)
# => #<PasswordLock:0x7f8e1b8c0b80 @password=1234>
कंपनी ने पासवर्ड को एन्क्रिप्ट करने का फैसला किया है ताकि वह सादे टेक्स्ट में संग्रहीत न रहे, और उसने आपसे इसे एन्क्रिप्ट करने के लिए एक मेथड बनाने को कहा है।
हर पासवर्ड टाइप के एन्क्रिप्ट होने का अपना तरीका है:
Int32 पासवर्ड: पासवर्ड को दो से भाग दिया जाता है और निकटतम पूर्णांक तक पूर्णांकित किया जाता है।String पासवर्ड: पासवर्ड को उलट दिया जाता है।Float64 पासवर्ड: पासवर्ड को चार से गुणा किया जाता है।encrypt नाम का एक इंस्टेंस मेथड बनाइए, जो कोई आर्गुमेंट नहीं लेता और @password इंस्टेंस वेरिएबल को इस तरह बदलता है कि वह एन्क्रिप्ट हो जाए।
password_lock = PasswordLock.new(1234)
# => #<PasswordLock:0x7f8e1b8c0b80 @password=1234>
password_lock.encrypt
# => #<PasswordLock:0x7f8e1b8c0b80 @password=617>
कंपनी ने आपसे एक ऐसा मेथड बनाने को भी कहा है जो जाँचता है कि दिया गया पासवर्ड सही है या नहीं।
वे चाहते हैं कि पासवर्ड गलत होने पर मेथड nil लौटाए और पासवर्ड सही होने पर "Unlocked" लौटाए।
unlock? नाम का एक इंस्टेंस मेथड बनाइए, जो जाँचने वाला पासवर्ड आर्गुमेंट के रूप में लेता है।
पासवर्ड गलत होने पर यह मेथड nil लौटाना चाहिए और पासवर्ड सही होने पर "Unlocked" लौटाना चाहिए।
password_lock = PasswordLock.new(1234)
password_lock.encrypt
password_lock.unlock?(1234)
# => "Unlocked"
Exercism पर साइन अप कीजिए और Crystal को 26 कॉन्सेप्ट142 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।