दृश्य # 1 और # 2 सामान्य रूप से गलत हैं।
- किसी भी प्रकार का डेटा-
* -> *
लेबल लेबल के रूप में काम कर सकता है, मोनैड्स इससे कहीं अधिक हैं।
- (
IO
मोनाड के अपवाद के साथ ) एक सन्यासी के भीतर गणना अशुद्ध नहीं है। वे केवल उन संगणनाओं का प्रतिनिधित्व करते हैं जिन्हें हम साइड इफेक्ट्स के रूप में देखते हैं, लेकिन वे शुद्ध हैं।
ये दोनों गलतफहमी IO
मोनाड पर ध्यान केंद्रित करने से आती हैं , जो वास्तव में थोड़ा खास है।
यदि संभव हो तो श्रेणी सिद्धांत में शामिल किए बिना, मैं # 3 पर थोड़ा विस्तार करने की कोशिश करूंगा।
मानक अभिकलन
एक कार्यात्मक प्रोग्रामिंग भाषा में सभी संगणना को स्रोत प्रकार और लक्ष्य प्रकार के साथ कार्यों के रूप में देखा जा सकता है f :: a -> b
:। यदि किसी फ़ंक्शन में एक से अधिक तर्क हैं, तो हम इसे करीने से एक-तर्क फ़ंक्शन में बदल सकते हैं ( हास्केल विकी भी देखें )। और अगर हम सिर्फ एक मूल्य है x :: a
(0 तर्क के साथ एक समारोह), हम इसे एक समारोह है कि का एक तर्क लेता है में बदल सकते हैं इकाई प्रकार : (\_ -> x) :: () -> a
।
हम .
ऑपरेटर का उपयोग करके इस तरह के कार्यों की रचना करके अधिक जटिल कार्यक्रमों को सरल बना सकते हैं । उदाहरण के लिए, यदि हमारे पास है f :: a -> b
और g :: b -> c
हम प्राप्त करते हैं g . f :: a -> c
। ध्यान दें कि यह हमारे रूपांतरित मूल्यों के लिए भी काम करता है: यदि हमारे पास है x :: a
और इसे हमारे प्रतिनिधित्व में बदल देता है, तो हम प्राप्त करते हैं f . ((\_ -> x) :: () -> a) :: () -> b
।
इस प्रतिनिधित्व के कुछ बहुत महत्वपूर्ण गुण हैं, अर्थात्:
- हमारे पास एक विशेष कार्य है - प्रत्येक प्रकार के लिए पहचान समारोह । यह सम्मान के साथ एक पहचान तत्व है : दोनों के लिए और बराबर है ।
id :: a -> a
a
.
f
f . id
id . f
- समारोह रचना ऑपरेटर
.
है साहचर्य ।
मोनडिक संगणनाएँ
मान लीजिए कि हम कुछ विशेष श्रेणी की संगणनाओं के साथ चयन और काम करना चाहते हैं, जिनके परिणाम में केवल एकल रिटर्न मान से अधिक कुछ है। हम निर्दिष्ट नहीं करना चाहते हैं कि "कुछ और" का क्या मतलब है, हम चीजों को यथासंभव सामान्य रखना चाहते हैं। "कुछ और" का प्रतिनिधित्व करने का सबसे सामान्य तरीका एक प्रकार के फ़ंक्शन के रूप में प्रतिनिधित्व कर रहा है - एक प्रकार m
का * -> *
(यानी यह एक प्रकार को दूसरे में परिवर्तित करता है)। इसलिए प्रत्येक श्रेणी की संगणना के लिए, जिसके साथ हम काम करना चाहते हैं, हमारे पास कुछ प्रकार के कार्य होंगे m :: * -> *
। (में हास्केल, m
है []
, IO
, Maybe
, आदि) और श्रेणी इच्छा प्रकार के सभी कार्यों में शामिल है a -> m b
।
अब हम इस तरह की श्रेणी में कार्यों के साथ उसी तरह काम करना चाहेंगे जैसे कि मूल मामले में। हम इन कार्यों की रचना करने में सक्षम होना चाहते हैं, हम चाहते हैं कि रचना सहयोगी हो, और हम एक पहचान बनाना चाहते हैं। ज़रुरत है:
- एक ऑपरेटर के लिए (चलो इसे कॉल करें
<=<
) जो फ़ंक्शन f :: a -> m b
और g :: b -> m c
कुछ के रूप में रचना करता है g <=< f :: a -> m c
। और, यह सहयोगी होना चाहिए।
- प्रत्येक प्रकार के लिए कुछ पहचान कार्य करने के लिए, आइए इसे कॉल करें
return
। हम भी चाहते हैं कि f <=< return
जैसा f
और जैसा है, वैसा ही हो return <=< f
।
कोई भी m :: * -> *
जिसके लिए हमारे पास ऐसे कार्य हैं return
और <=<
उसे एक मोनाड कहा जाता है । यह हमें सरल मामलों से जटिल गणनाएँ बनाने की अनुमति देता है, जैसे कि मूल मामले में, लेकिन अब रिटर्न वैल्यूज़ के प्रकारों को बदल दिया जाता है m
।
(वास्तव में, मैंने यहां शब्द श्रेणी का थोड़ा दुरुपयोग किया है। श्रेणी-सिद्धांत अर्थ में हम अपने निर्माण को एक श्रेणी कह सकते हैं, जब हम जानते हैं कि यह इन कानूनों का पालन करता है।)
हास्केल में मठ
हास्केल (और अन्य कार्यात्मक भाषाओं) में हम ज्यादातर मूल्यों के साथ काम करते हैं, न कि प्रकारों के कार्यों के साथ () -> a
। इसलिए <=<
प्रत्येक मोनाड को परिभाषित करने के बजाय , हम एक फ़ंक्शन को परिभाषित करते हैं (>>=) :: m a -> (a -> m b) -> m b
। ऐसी वैकल्पिक परिभाषा समतुल्य है, हम इसका >>=
उपयोग <=<
और इसके विपरीत व्यक्त कर सकते हैं (एक अभ्यास के रूप में प्रयास करें, या स्रोतों को देखें )। सिद्धांत अब कम स्पष्ट है, लेकिन यह समान है: हमारे परिणाम हमेशा प्रकार के होते हैं m a
और हम प्रकारों के कार्यों की रचना करते हैं a -> m b
।
हमारे द्वारा बनाए गए प्रत्येक मोनाड के लिए, हमें यह जांचना नहीं भूलना चाहिए return
और हमारे <=<
पास आवश्यक गुण हैं: सहानुभूति और बाएं / दाएं पहचान। का उपयोग करके व्यक्त किया गया return
और >>=
उन्हें मोनाड कानून कहा जाता है ।
एक उदाहरण - सूचियाँ
यदि हम m
होना चुनते हैं []
, तो हमें प्रकार के कार्यों की एक श्रेणी मिलती है a -> [b]
। ऐसे कार्य गैर-नियतात्मक संगणनाओं का प्रतिनिधित्व करते हैं, जिनके परिणाम एक या अधिक मूल्य हो सकते हैं, लेकिन यह भी कोई मूल्य नहीं है। यह तथाकथित सूची मोनाद को जन्म देता है । की संरचना f :: a -> [b]
और g :: b -> [c]
निम्नानुसार काम करती है: प्रकार के g <=< f :: a -> [c]
सभी संभावित परिणामों की गणना करने का मतलब है [b]
, g
उनमें से प्रत्येक पर लागू होता है, और सभी परिणामों को एक सूची में इकट्ठा करता है। हास्केल में व्यक्त किया
return :: a -> [a]
return x = [x]
(<=<) :: (b -> [c]) -> (a -> [b]) -> (a -> [c])
g (<=<) f = concat . map g . f
या उपयोग कर रहा है >>=
(>>=) :: [a] -> (a -> [b]) -> [b]
x >>= f = concat (map f x)
ध्यान दें कि इस उदाहरण में रिटर्न प्रकार इस प्रकार थे [a]
कि यह संभव था कि उनके पास किसी प्रकार का मूल्य न हो a
। वास्तव में, एक सन्यासी के लिए ऐसी कोई आवश्यकता नहीं होती है कि रिटर्न प्रकार में ऐसे मान होने चाहिए। कुछ साधुओं के पास हमेशा (जैसे IO
या State
) होते हैं, लेकिन कुछ पसंद नहीं करते हैं []
या जैसे होते हैं Maybe
।
आईओ मोनद
जैसा कि मैंने उल्लेख किया है, IO
सन्यासी कुछ विशेष है। प्रकार के मूल्य का IO a
अर्थ है a
कार्यक्रम के वातावरण के साथ बातचीत करके निर्मित प्रकार का मूल्य । इसलिए (सभी अन्य साधुओं के विपरीत), हम IO a
कुछ शुद्ध निर्माण का उपयोग करके प्रकार के मूल्य का वर्णन नहीं कर सकते हैं । यहां IO
केवल एक टैग या एक लेबल है जो पर्यावरण के साथ बातचीत करने वाले कम्प्यूटेशंस को अलग करता है। यह (एकमात्र मामला) है जहां विचार # 1 और # 2 सही हैं।
के लिए IO
इकाई:
- संरचना
f :: a -> IO b
और g :: b -> IO c
साधन: गणना f
जो पर्यावरण के साथ सहभागिता करता है, और फिर गणना g
करता है जो मूल्य का उपयोग करता है और पर्यावरण के साथ बातचीत करने वाले परिणाम की गणना करता है।
return
बस IO
"टैग" को मूल्य में जोड़ता है (हम पर्यावरण को बरकरार रखते हुए परिणाम को "गणना" करते हैं)।
- संन्यासी द्वारा मोनाड कानूनों (संघात्मकता, पहचान) की गारंटी दी जाती है।
कुछ नोट:
- चूंकि मोनाडिक अभिकलन में हमेशा परिणाम प्रकार होता है
m a
, इसलिए कोई तरीका नहीं है कि कैसे IO
मोनाड से "बच" जाए । अर्थ यह है: एक बार एक संगणना पर्यावरण के साथ सहभागिता करती है, आप उससे एक अभिकलन का निर्माण नहीं कर सकते।
- जब एक कार्यात्मक प्रोग्रामर कुछ नहीं जानता कि कैसे शुद्ध तरीके से कुछ बनाना है, तो वह ( अंतिम उपाय के रूप में )
IO
मोनाड के भीतर कुछ स्टेटफुल कम्प्यूटेशन द्वारा कार्य को कर सकता है । यही कारण है कि IO
अक्सर एक प्रोग्रामर के पाप बिन कहा जाता है ।
- ध्यान दें कि एक अशुद्ध दुनिया में (कार्यात्मक प्रोग्रामिंग के अर्थ में) एक मान पढ़ने से पर्यावरण भी बदल सकता है (जैसे उपयोगकर्ता के इनपुट का उपभोग करना)। इसीलिए जैसे कार्यों
getChar
का एक परिणाम प्रकार होना चाहिए IO something
।