आश्रित टाइप्ड हास्केल, अब?
हास्केल कुछ हद तक, एक भरोसेमंद टाइप की भाषा है। टाइप-लेवल डेटा की एक धारणा है, अब अधिक समझदारी से टाइप करने के लिए धन्यवाद DataKinds
, और GADTs
टाइप-लेवल डेटा को रन-टाइम प्रतिनिधित्व देने के लिए कुछ साधन ( ) हैं। इसलिए, रन-टाइम सामान के मूल्य प्रभावी रूप से प्रकारों में दिखाई देते हैं , जो कि एक भाषा के लिए निर्भरता से टाइप करने के लिए इसका मतलब है।
सरल डेटाटिप्स हैं स्तर तक प्रचारित किया जाता है, ताकि उनके द्वारा निहित मूल्यों का उपयोग प्रकारों में किया जा सके। इसलिए आर्कटिक उदाहरण
data Nat = Z | S Nat
data Vec :: Nat -> * -> * where
VNil :: Vec Z x
VCons :: x -> Vec n x -> Vec (S n) x
संभव हो जाता है, और इसके साथ, जैसे परिभाषाएँ
vApply :: Vec n (s -> t) -> Vec n s -> Vec n t
vApply VNil VNil = VNil
vApply (VCons f fs) (VCons s ss) = VCons (f s) (vApply fs ss)
जो अच्छा है। ध्यान दें कि लंबाईn
उस फ़ंक्शन में एक विशुद्ध रूप से स्थिर चीज़ है, यह सुनिश्चित करते हुए कि इनपुट और आउटपुट वैक्टर की लंबाई समान है, भले ही उस लंबाई के निष्पादन में कोई भूमिका न हो
vApply
। इसके विपरीत, यह बहुत जटिल काम (यानी, असंभव) समारोह है जो लागू करने के लिए है n
एक दिया की प्रतियां x
(होगा जो pure
करने के लिए vApply
s ' <*>
)
vReplicate :: x -> Vec n x
क्योंकि यह जानना महत्वपूर्ण है कि रन-टाइम में कितनी प्रतियां बनानी हैं। एकल दर्ज करें।
data Natty :: Nat -> * where
Zy :: Natty Z
Sy :: Natty n -> Natty (S n)
किसी भी प्रकार के प्रचार के लिए, हम अपने मूल्यों के रन-टाइम डुप्लिकेट द्वारा बसाए गए एकल प्रकार के एकल परिवार को बढ़ावा दे सकते हैं। Natty n
प्रकार-स्तर की रन-टाइम प्रतियों का प्रकार है n
:: Nat
। हम अब लिख सकते हैं
vReplicate :: Natty n -> x -> Vec n x
vReplicate Zy x = VNil
vReplicate (Sy n) x = VCons x (vReplicate n x)
इसलिए आपके पास रन-टाइम मान के लिए एक प्रकार-स्तरीय मान है: रन-टाइम कॉपी का निरीक्षण करना टाइप-स्तर मान के स्थिर ज्ञान को परिष्कृत करता है। भले ही शब्द और प्रकार अलग हो गए हों, हम एक प्रकार के एपॉक्सी राल के रूप में सिंगलटन निर्माण का उपयोग करके चरणबद्ध तरीके से बॉन्ड बनाते हुए एक भरोसेमंद रूप से काम कर सकते हैं। यह प्रकारों में मनमाने ढंग से चलने की अभिव्यक्ति की अनुमति देने से एक लंबा रास्ता है, लेकिन यह कुछ भी नहीं है।
बुरा क्या है? क्या कमी है?
आइए इस तकनीक पर थोड़ा दबाव डालें और देखें कि क्या शुरू होता है। हमें यह अंदाजा हो सकता है कि सिंगल को थोड़ा और अधिक आसानी से मैनेज किया जा सकता है
class Nattily (n :: Nat) where
natty :: Natty n
instance Nattily Z where
natty = Zy
instance Nattily n => Nattily (S n) where
natty = Sy natty
हमें लिखने, कहने,
instance Nattily n => Applicative (Vec n) where
pure = vReplicate natty
(<*>) = vApply
यह काम करता है, लेकिन इसका मतलब है कि हमारे मूल Nat
प्रकार ने तीन प्रतियों को जन्म दिया है: एक प्रकार, एक एकल परिवार और एक एकल वर्ग। हमारे पास स्पष्ट Natty n
मूल्यों और Nattily n
शब्दकोशों के आदान-प्रदान के लिए एक बल्कि क्लूनी प्रक्रिया है । इसके अलावा,Natty
ऐसा नहीं है Nat
: हमारे पास रन-टाइम मानों पर निर्भरता के कुछ प्रकार हैं, लेकिन उस प्रकार पर नहीं जिस पर हमने पहले सोचा था। कोई भी पूरी तरह से निर्भर प्रकार की भाषा निर्भर प्रकारों को जटिल नहीं बनाती है!
इस बीच, हालांकि Nat
प्रचार किया जा सकता है, Vec
नहीं। आप अनुक्रमित प्रकार द्वारा अनुक्रमण नहीं कर सकते। भरोसेमंद टाइप की गई भाषाओं पर पूर्णतया कोई प्रतिबंध नहीं है, और मेरे करियर में एक भरोसेमंद टाइप-शो के रूप में, मैंने अपनी बातों में दो-परत अनुक्रमण के उदाहरणों को शामिल करना सीखा है, केवल उन लोगों को पढ़ाने के लिए जिन्होंने एक-परत अनुक्रमण किया है। मुश्किल-लेकिन-संभव नहीं मुझे कार्ड के घर की तरह गुना करने की उम्मीद है। समस्या क्या है? समानता। जब आप कंस्ट्रक्टर्स को स्पष्ट रूप से समान मांगों में एक विशिष्ट रिटर्न प्रकार देते हैं तो जीएडीटी आपके द्वारा प्राप्त बाधाओं का अनुवाद करके काम करता है। इस प्रकार सं।
data Vec (n :: Nat) (x :: *)
= n ~ Z => VNil
| forall m. n ~ S m => VCons x (Vec m x)
हमारे प्रत्येक दो समीकरणों में, दोनों पक्ष दयालु हैं Nat
।
अब वैक्टर पर अनुक्रमित कुछ के लिए एक ही अनुवाद का प्रयास करें।
data InVec :: x -> Vec n x -> * where
Here :: InVec z (VCons z zs)
After :: InVec z ys -> InVec z (VCons y ys)
हो जाता है
data InVec (a :: x) (as :: Vec n x)
= forall m z (zs :: Vec x m). (n ~ S m, as ~ VCons z zs) => Here
| forall m y z (ys :: Vec x m). (n ~ S m, as ~ VCons y ys) => After (InVec z ys)
और अब हम समतुल्य बाधाओं को as :: Vec n x
और के
बीच बनाते हैंVCons z zs :: Vec (S m) x
दोनों पक्षों के जहाँ दोनों पक्ष वाक्यगत रूप से अलग (लेकिन समान रूप से) भिन्न होते हैं। जीएचसी कोर वर्तमान में ऐसी अवधारणा से सुसज्जित नहीं है!
और क्या याद आ रही है? खैर, अधिकांश हस्केल टाइप स्तर से गायब है। जिन शब्दों को आप प्रमोट कर सकते हैं उनकी भाषा में बस चर और गैर-जीएडीटी निर्माता हैं, वास्तव में। एक बार आपके पास होने के बाद, type family
मशीनरी आपको टाइप-लेवल प्रोग्राम लिखने की अनुमति देती है: उनमें से कुछ काफी ऐसे कार्य हो सकते हैं, जिन्हें आप टर्म स्तर पर लिखने पर विचार करेंगे (उदाहरण के लिए, Nat
इसके अलावा लैस करना, ताकि आप इसके लिए एक अच्छा प्रकार दे सकें Vec
) , लेकिन यह सिर्फ एक संयोग है!
एक और बात याद आ रही है, व्यवहार में, एक पुस्तकालय है जो मूल्यों द्वारा सूचकांक प्रकारों के लिए हमारी नई क्षमताओं का उपयोग करता है। इस बहादुर नई दुनिया में क्या करें Functor
और Monad
बनें? मैं इसके बारे में सोच रहा हूं, लेकिन अभी भी बहुत कुछ करना बाकी है।
प्रकार-स्तरीय कार्यक्रम चलाना
हास्केल, ज्यादातर भरोसेमंद टाइपिंग प्रोग्रामिंग भाषाओं की तरह, दो
परिचालन शब्दार्थ हैं। जिस तरह से रन-टाइम सिस्टम प्रोग्राम चलाता है (टाइप एक्ट्रेसेज, टाइप एरास के बाद, अत्यधिक अनुकूलित) और उसके बाद टाइपकार्चर प्रोग्राम (आपके टाइप परिवारों, आपके "टाइप क्लास प्रोलॉग" को ओपन एक्सप्रेशन के साथ) चलाता है। हास्केल के लिए, आप आम तौर पर दोनों को मिलाते नहीं हैं, क्योंकि निष्पादित होने वाले कार्यक्रम विभिन्न भाषाओं में हैं। भरोसेमंद रूप से टाइप की गई भाषाओं में अलग-अलग रन-टाइम और एक ही में प्रोग्राम भाषा के हैं, लेकिन चिंता न करें, रन-टाइम मॉडल अभी भी आपको टाइप एरेस करने देता है और वास्तव में, प्रूफ इरेज़र: यही Coq's है अर्क है तंत्र के लिए स्थिर निष्पादन मॉडल आपको देता है; कम से कम एडविन ब्रैडी का कंपाइलर करता है (हालांकि एडविन अनावश्यक रूप से डुप्लिकेट मानों को मिटाता है, साथ ही प्रकार और सबूत)। चरण भेद वाक्य-रचना श्रेणी का अंतर नहीं हो सकता है
किसी भी समय , लेकिन यह जीवित और अच्छी तरह से है।
कुल मिलाकर टाइप की जाने वाली भाषाएं, कुल मिलाकर, टाइपटेकर को लंबे इंतजार से बदतर किसी भी चीज के भय से मुक्त कार्यक्रम चलाने की अनुमति देती हैं। जैसा कि हास्केल अधिक निर्भरता से टाइप करता है, हम इस सवाल का सामना करते हैं कि इसका स्थिर निष्पादन मॉडल क्या होना चाहिए? एक दृष्टिकोण कुल कार्यों के लिए स्थैतिक निष्पादन को प्रतिबंधित करना हो सकता है, जो हमें समान स्वतंत्रता को चलाने की अनुमति देगा, लेकिन हमें डेटा और कोडाटा के बीच अंतर (कम से कम प्रकार के स्तर के लिए) बनाने के लिए मजबूर कर सकता है, ताकि हम बता सकें कि क्या समाप्ति या उत्पादकता लागू करें। लेकिन यह एकमात्र तरीका नहीं है। हम एक बहुत ही कमजोर निष्पादन मॉडल का चयन करने के लिए स्वतंत्र हैं, जो प्रोग्राम चलाने के लिए अनिच्छुक है, कम से कम समीकरण बनाने की लागत से ही गणना होती है। और वास्तव में, यही जीएचसी वास्तव में करता है। GHC कोर के टाइपिंग नियमों का कोई उल्लेख नहीं है चलाने
कार्यक्रम, लेकिन केवल समीकरणों के लिए साक्ष्य की जाँच के लिए। कोर में अनुवाद करते समय, जीएचसी की बाधा सॉल्वर आपके प्रकार के स्तर के कार्यक्रमों को चलाने की कोशिश करता है, जिससे साक्ष्य का थोड़ा सा चांदी का निशान पैदा होता है जो किसी दिए गए अभिव्यक्ति को उसके सामान्य रूप के बराबर होता है। यह सबूत-जनरेशन विधि थोड़ी अप्रत्याशित और अनिवार्य रूप से अधूरी है: यह डरावनी दिखने वाली पुनरावृत्ति से लड़ती है, उदाहरण के लिए, और यह शायद बुद्धिमान है। एक बात जिस पर हमें चिंता करने की ज़रूरत नहीं IO
है वह है टाइपकैचर में कम्प्यूटेशन का निष्पादन : याद रखें कि टाइप-टेकर को launchMissiles
एक ही अर्थ नहीं देना
है कि रन-टाइम सिस्टम क्या करता है!
Hindley-मिलनर कल्चर
हिंडले-मिलनर प्रकार प्रणाली दुर्भाग्यपूर्ण सांस्कृतिक दुष्परिणामों के साथ, चार विशिष्ट भेदों के वास्तव में भयानक संयोग को प्राप्त करती है, जो कि कई लोग भेदों के बीच भेद नहीं देख सकते हैं और संयोग को अपरिहार्य मानते हैं! मैं किस बारे में बात कर रहा हूं?
- शब्द बनाम प्रकार
- स्पष्ट रूप से लिखित बातें बनाम निहित लिखित बातें
- रन-टाइम में उपस्थिति बनाम से पहले रन-टाइम विलोपन
- गैर-निर्भर अमूर्त बनाम निर्भर मात्रा का ठहराव
हम शब्दों को लिखने और टाइप करने के लिए आदी होने के लिए उपयोग कर रहे हैं ... और फिर मिट गए। हम इसी प्रकार के अमूर्त और अनुप्रयोग के साथ प्रकार चर पर मात्रात्मक रूप से और सांख्यिकीय रूप से हो रहे हैं।
इन भेदों को संरेखण से बाहर आने से पहले आपको वेनिला हिंडले-मिलनर से बहुत दूर नहीं जाना है, और यह कोई बुरी बात नहीं है । एक शुरुआत के लिए, हमारे पास और अधिक दिलचस्प प्रकार हो सकते हैं यदि हम उन्हें कुछ स्थानों पर लिखने के लिए तैयार हैं। इस बीच, जब हम अतिभारित कार्यों का उपयोग करते हैं, तो हमें टाइप क्लास डिक्शनरी लिखना नहीं आता है, लेकिन रन-टाइम पर वे डिक्शनरी निश्चित रूप से मौजूद होती हैं (या इनबिल्ट होती हैं)। भरोसेमंद रूप से टाइप की जाने वाली भाषाओं में, हम रन-टाइम पर सिर्फ टाइप्स से ज्यादा मिटा देने की उम्मीद करते हैं, लेकिन (टाइप क्लासेस के साथ) कि कुछ अनुमानित मानों को मिटाया नहीं जाएगा। उदाहरण के लिए, vReplicate
सांख्यिक तर्क अक्सर वांछित वेक्टर के प्रकार से अनुमान लगाने योग्य होता है, लेकिन हमें अभी भी इसे रन-टाइम पर जानना होगा।
हमें कौन से भाषा डिज़ाइन विकल्पों की समीक्षा करनी चाहिए क्योंकि ये संयोग अब धारण नहीं करते हैं? उदाहरण के लिए, क्या यह सही है कि हास्केल forall x. t
स्पष्ट रूप से एक क्वांटिफायर को इंस्टेंट करने का कोई तरीका प्रदान नहीं करता है ? यदि टाइपराइटर यूनिफ़ाइंग x
द्वारा अनुमान नहीं लगा सकता है t
, तो हमारे पास यह कहने का कोई अन्य तरीका नहीं है कि क्या x
होना चाहिए।
मोटे तौर पर, हम एक अखंड अवधारणा के रूप में "प्रकार के अनुमान" का इलाज नहीं कर सकते हैं कि हमारे पास सभी या कुछ भी नहीं है। एक शुरुआत के लिए, हमें "सामान्यीकरण" पहलू (मिलनर के "लेट" नियम) को विभाजित करने की आवश्यकता है, जो कि प्रतिबंधित करने पर बहुत अधिक निर्भर करता है कि कौन से प्रकार यह सुनिश्चित करने के लिए मौजूद हैं कि एक बेवकूफ मशीन "विशेषज्ञता" पहलू (मिल्नर) के संस्करण से अनुमान लगा सकती है "नियम) जो आपके बाधा समाधानकर्ता के रूप में प्रभावी है। हम उम्मीद कर सकते हैं कि शीर्ष-स्तरीय प्रकार अनुमान लगाने में कठिन हो जाएंगे, लेकिन आंतरिक प्रकार की जानकारी प्रचारित करने में काफी आसान रहेगी।
हास्केल के लिए अगला कदम
हम देख रहे हैं कि प्रकार और तरह के स्तर बहुत समान हैं (और वे पहले से ही जीएचसी में एक आंतरिक प्रतिनिधित्व साझा करते हैं)। हम उन्हें भी मिला सकते हैं। * :: *
यदि हम कर सकते हैं तो यह लेना मजेदार होगा : हमने तार्किक ध्वनिशीलता बहुत पहले खो
दी थी, जब हमने नीचे की अनुमति दी थी, लेकिन टाइप
साउंडनेस आमतौर पर एक कमजोर आवश्यकता होती है। हमें जांच करनी चाहिए। यदि हमारे पास अलग-अलग प्रकार, प्रकार, आदि के स्तर होने चाहिए, तो हम कम से कम यह सुनिश्चित कर सकते हैं कि सभी प्रकार के स्तर और उससे ऊपर हमेशा प्रचार किया जा सकता है। यह केवल उस प्रकार के बहुरूपता का पुनः उपयोग करने के लिए बहुत अच्छा होगा जो हमारे पास पहले से ही प्रकार के लिए है, बजाय इस तरह के स्तर पर फिर से बहुरूपता का आविष्कार करने के।
हमें विषम समीकरणों की अनुमति देकर बाधाओं की वर्तमान प्रणाली को सरल और सामान्य बनाना चाहिए a ~ b
जहां प्रकार a
और
b
समान रूप से समान नहीं हैं (लेकिन बराबर सिद्ध किया जा सकता है)। यह एक पुरानी तकनीक है (मेरी थीसिस में, पिछली शताब्दी में) जो निर्भरता को आसान बनाता है। हम GADTs में अभिव्यक्तियों पर अड़चनें व्यक्त करने में सक्षम होंगे, और इस प्रकार जो प्रचारित किया जा सकता है उस पर प्रतिबंधों को शिथिल करेंगे।
हमें एक आश्रित फ़ंक्शन प्रकार की शुरुआत करके सिंगलटन निर्माण की आवश्यकता को समाप्त करना चाहिए pi x :: s -> t
। इस प्रकार के एक फ़ंक्शन को स्पष्ट रूप से उस प्रकार की किसी भी अभिव्यक्ति पर लागू किया जा सकता है s
जो टाइप और टर्म लैंग्वेज के चौराहे पर रहता है (इसलिए, चर, कंस्ट्रक्टर, बाद में आने के लिए और अधिक के साथ)। इसी लैम्बडा और एप्लिकेशन को रन-टाइम में मिटाया नहीं जाएगा, इसलिए हम लिख पाएंगे
vReplicate :: pi n :: Nat -> x -> Vec n x
vReplicate Z x = VNil
vReplicate (S n) x = VCons x (vReplicate n x)
Nat
द्वारा प्रतिस्थापित किए बिना Natty
। डोमेन pi
किसी भी प्रकार का प्रचारक हो सकता है, इसलिए यदि GADT को बढ़ावा दिया जा सकता है, तो हम आश्रित क्वांटिफायर अनुक्रम (या "दूरबीन" लिख सकते हैं, जैसा कि डी ब्रूइजन ने उन्हें बुलाया)
pi n :: Nat -> pi xs :: Vec n x -> ...
हमें जो भी लंबाई चाहिए।
इन कदमों का उद्देश्य कमजोर औजारों और क्लॉन्की एनकोडिंग के साथ काम करने के बजाय अधिक सामान्य उपकरणों के साथ सीधे काम करके जटिलता को खत्म करना है। मौजूदा आंशिक खरीद, हास्केल के प्रकारों पर निर्भरता के प्रकारों के लाभों को और अधिक महंगा बनाती है, जो उन्हें होने की आवश्यकता है।
बहुत कठिन?
आश्रित प्रकार बहुत से लोगों को परेशान करते हैं। वे मुझे नर्वस करते हैं, लेकिन मुझे नर्वस होना पसंद है, या कम से कम मुझे वैसे भी नर्वस होना मुश्किल लगता है। लेकिन यह मदद नहीं करता है कि इस विषय के चारों ओर अज्ञानता का एक बहुत कोहरा है। इस तथ्य के कारण कि हम सभी को अभी भी बहुत कुछ सीखना बाकी है। लेकिन कम कट्टरपंथी दृष्टिकोण के समर्थकों को यह सुनिश्चित करने के लिए जाना जाता है कि आश्रित प्रकार के डर को हमेशा बिना यह सुनिश्चित किए कि उनके साथ तथ्य पूर्ण हैं। मैं नाम नहीं बताऊंगा। ये "अनुचित टाइपकास्टिंग", "ट्यूरिंग अधूरा", "कोई चरण भेद नहीं", "कोई प्रकार का क्षरण नहीं", "हर जगह सबूत", आदि, मिथक बने रहते हैं, भले ही वे बकवास हो।
यह निश्चित रूप से ऐसा नहीं है कि भरोसेमंद रूप से टाइप किए गए प्रोग्राम हमेशा सही साबित होने चाहिए। किसी के कार्यक्रमों की बुनियादी स्वच्छता में सुधार कर सकते हैं, एक पूर्ण विनिर्देश के सभी रास्ते में जाने के बिना प्रकार में अतिरिक्त आक्रमणकारियों को लागू करना। इस दिशा में छोटे कदमों के परिणामस्वरूप अक्सर कुछ या कोई अतिरिक्त प्रमाण दायित्वों के साथ बहुत मजबूत गारंटी मिलती है। यह सच नहीं है कि निर्भरता से टाइप किए गए प्रोग्राम अनिवार्य रूप से प्रमाणों से भरे होते हैं , वास्तव में मैं आमतौर पर मेरी परिभाषा में किसी भी प्रमाण की उपस्थिति को क्यू के रूप में लेता हूं ताकि मेरी परिभाषाओं पर सवाल उठाया जा सके ।
कलात्मकता में किसी भी वृद्धि के साथ, हम बेईमानी के साथ-साथ नई चीजों को भी कहने के लिए स्वतंत्र हो जाते हैं। उदाहरण के लिए, द्विआधारी खोज पेड़ों को परिभाषित करने के लिए बहुत सारे आपराधिक तरीके हैं, लेकिन इसका मतलब यह नहीं है कि एक अच्छा तरीका नहीं है । यह महत्वपूर्ण है कि बुरा अनुभव को बेहतर नहीं माना जा सकता है, भले ही वह इसे स्वीकार करने के लिए अहंकार को जन्म दे। आश्रित परिभाषाओं का डिजाइन एक नया कौशल है जो सीखने में मदद करता है, और हास्केल प्रोग्रामर होने के नाते स्वचालित रूप से आपको एक विशेषज्ञ नहीं बनाता है! और अगर कुछ कार्यक्रम बेईमानी हैं, तो भी आप दूसरों को निष्पक्ष होने की स्वतंत्रता से वंचित क्यों करेंगे?
हास्केल के साथ फिर भी परेशान क्यों?
मैं वास्तव में निर्भर प्रकारों का आनंद लेता हूं, लेकिन मेरे हैकिंग के अधिकांश प्रोजेक्ट अभी भी हास्केल में हैं। क्यों? हास्केल के प्रकार वर्ग हैं। हास्केल में उपयोगी पुस्तकालय हैं। हास्केल में प्रभाव के साथ प्रोग्रामिंग का एक व्यावहारिक (हालांकि आदर्श से बहुत दूर) उपचार है। हास्केल में एक औद्योगिक शक्ति संकलक है। बढ़ती समुदाय और बुनियादी ढाँचे में निर्भरता से टाइप की गई भाषाएँ बहुत पहले की अवस्था में हैं, लेकिन हम वहाँ पहुँचेंगे, जो कि संभव हो सकता है, जैसे कि, उदाहरण के तौर पर, मेटाप्रोग्रामिंग और डेटाटाइप जेनरिक के माध्यम से। लेकिन आपको बस इस बात पर ध्यान देना होगा कि लोग हास्केल के आश्रित प्रकार के कदमों के परिणामस्वरूप क्या कर रहे हैं, यह देखने के लिए कि वर्तमान पीढ़ी को आगे बढ़ाकर बहुत अधिक लाभ प्राप्त करना है।