GADT के स्पष्ट और बेहतर वाक्यविन्यास प्रदान करने के लिए निहित फोर्ल्स प्रदान करके अस्तित्वगत प्रकारों का उपयोग कर कोड करें
मुझे लगता है कि सामान्य समझौता है कि जीएडीटी सिंटैक्स बेहतर है। मैं यह नहीं कहूंगा कि यह इसलिए है क्योंकि जीएडीटी अंतर्निहित फ़ॉर्म्स प्रदान करते हैं, बल्कि इसलिए कि मूल सिंटैक्स, ExistentialQuantification
एक्सटेंशन के साथ सक्षम किया गया है, संभावित रूप से भ्रमित / भ्रामक है। यह वाक्यविन्यास, निश्चित रूप से, जैसा दिखता है:
data SomeType = forall a. SomeType a
या एक बाधा के साथ:
data SomeShowableType = forall a. Show a => SomeShowableType a
और मुझे लगता है कि आम सहमति यह है कि forall
यहां कीवर्ड का उपयोग पूरी तरह से अलग प्रकार के साथ आसानी से भ्रमित होने की अनुमति देता है:
data AnyType = AnyType (forall a. a) -- need RankNTypes extension
एक बेहतर वाक्यविन्यास ने एक अलग exists
कीवर्ड का उपयोग किया होगा , इसलिए आप लिखेंगे:
data SomeType = SomeType (exists a. a) -- not valid GHC syntax
GADT सिंटैक्स, जो अंतर्निहित या स्पष्ट रूप से उपयोग किया जाता है forall
, इन प्रकारों में अधिक समान है, और समझने में आसान लगता है। एक स्पष्ट के साथ भी forall
, निम्नलिखित परिभाषा इस विचार के पार हो जाती है कि आप किसी भी प्रकार का मान ले सकते हैं a
और इसे एक मोनोमोर्फ के अंदर डाल सकते हैं SomeType'
:
data SomeType' where
SomeType' :: forall a. (a -> SomeType') -- parentheses optional
और उस प्रकार के अंतर को देखना और समझना आसान है:
data AnyType' where
AnyType' :: (forall a. a) -> AnyType'
अस्तित्वगत प्रकारों में उनकी रुचि के प्रकार नहीं प्रतीत होते हैं, लेकिन उनका मिलान करने वाले पैटर्न कहते हैं कि कुछ प्रकार मौजूद हैं जिन्हें हम नहीं जानते कि यह किस प्रकार का है और जब तक हम टाइप करने योग्य या डेटा का उपयोग नहीं करते हैं।
हम उन्हें तब उपयोग करते हैं जब हम प्रकारों को छिपाना चाहते हैं (उदा: विषम सूची के लिए) या हमें वास्तव में पता नहीं है कि कंपोजिट समय पर क्या प्रकार हैं।
मुझे लगता है कि ये बहुत दूर नहीं हैं, हालांकि आपको अस्तित्व के प्रकारों का उपयोग Typeable
या उपयोग करने की आवश्यकता नहीं है Data
। मुझे लगता है कि यह कहना अधिक सटीक होगा कि अस्तित्वगत प्रकार एक अनिर्दिष्ट प्रकार के आसपास एक अच्छी तरह से टाइप किया हुआ "बॉक्स" प्रदान करता है। बॉक्स एक अर्थ में प्रकार को "छिपा" करता है, जो आपको ऐसे बक्से की एक विषम सूची बनाने की अनुमति देता है, जिसमें शामिल प्रकारों की अनदेखी होती है। यह पता चला है कि SomeType'
ऊपर की तरह एक अप्रतिबंधित अस्तित्व, बहुत बेकार है, लेकिन एक विवश प्रकार:
data SomeShowableType' where
SomeShowableType' :: forall a. (Show a) => a -> SomeShowableType'
आपको "बॉक्स" के अंदर झाँकने के लिए पैटर्न मिलान करने की अनुमति देता है और प्रकार श्रेणी की सुविधाएँ उपलब्ध कराता है:
showIt :: SomeShowableType' -> String
showIt (SomeShowableType' x) = show x
ध्यान दें कि यह किसी भी प्रकार के वर्ग के लिए काम करता है, न कि केवल Typeable
या Data
।
स्लाइड डेक के पेज 20 के बारे में आपके भ्रम के संबंध में, लेखक कह रहा है कि यह एक फ़ंक्शन के लिए असंभव है जो किसी विशेष उदाहरण की मांग करने के लिए एक अस्तित्व लेता है । आप एक विशेष प्रकार का उपयोग करके एक फ़ंक्शन लिख सकते हैं , जैसे :Worker
Worker
Buffer
Worker
Buffer
MemoryBuffer
class Buffer b where
output :: String -> b -> IO ()
data Worker x = forall b. Buffer b => Worker {buffer :: b, input :: x}
data MemoryBuffer = MemoryBuffer
instance Buffer MemoryBuffer
memoryWorker = Worker MemoryBuffer (1 :: Int)
memoryWorker :: Worker Int
लेकिन यदि आप एक फ़ंक्शन लिखते हैं जो एक Worker
तर्क के रूप में लेता है , तो यह केवल सामान्य Buffer
प्रकार की क्लास सुविधाओं (जैसे, फ़ंक्शन output
) का उपयोग कर सकता है:
doWork :: Worker Int -> IO ()
doWork (Worker b x) = output (show x) b
यह मांग करने की कोशिश नहीं कर b
सकता कि पैटर्न मिलान के माध्यम से भी एक विशेष प्रकार के बफर बनें:
doWorkBroken :: Worker Int -> IO ()
doWorkBroken (Worker b x) = case b of
MemoryBuffer -> error "try this" -- type error
_ -> error "try that"
अंत में, अस्तित्वगत प्रकारों के बारे में रनटाइम जानकारी शामिल किए गए टाइपकास्ट के लिए निहित "शब्दकोश" तर्कों के माध्यम से उपलब्ध कराई गई है। Worker
प्रकार ऊपर बफर और इनपुट के लिए खेतों होने के अलावा, भी एक अदृश्य निहित क्षेत्र के लिए अंक कि है Buffer
शब्दकोश (कुछ v-मेज की तरह है, हालांकि यह शायद ही बहुत बड़ी है, यह सिर्फ उचित करने के लिए एक सूचक होता है के रूप में output
समारोह)।
आंतरिक रूप से, टाइप क्लास Buffer
को फ़ंक्शन फ़ील्ड्स के साथ डेटा प्रकार के रूप में दर्शाया जाता है, और उदाहरण इस प्रकार के "शब्दकोशों" हैं:
data Buffer' b = Buffer' { output' :: String -> b -> IO () }
dBuffer_MemoryBuffer :: Buffer' MemoryBuffer
dBuffer_MemoryBuffer = Buffer' { output' = undefined }
अस्तित्वगत प्रकार में इस शब्दकोश के लिए एक छिपा हुआ क्षेत्र है:
data Worker' x = forall b. Worker' { dBuffer :: Buffer' b, buffer' :: b, input' :: x }
और एक ऐसा कार्य जो doWork
अस्तित्वगत Worker'
मूल्यों पर कार्य करता है जैसे कि कार्यान्वित किया जाता है:
doWork' :: Worker' Int -> IO ()
doWork' (Worker' dBuf b x) = output' dBuf (show x) b
केवल एक फ़ंक्शन के साथ एक प्रकार के वर्ग के लिए, शब्दकोश वास्तव में एक न्यूटाइप के लिए अनुकूलित है, इसलिए इस उदाहरण में, अस्तित्वगत Worker
प्रकार में एक छिपी फ़ील्ड शामिल है जिसमें output
बफर के लिए फ़ंक्शन के लिए फ़ंक्शन पॉइंटर शामिल है , और यह केवल रनटाइम जानकारी की आवश्यकता है द्वारा doWork
।