हाल ही में हास्केल को संक्षेप में देखने पर, एक संक्षिप्त, संक्षिप्त, व्यावहारिक स्पष्टीकरण क्या होगा जो अनिवार्य रूप से एक सनक है?
मुझे सबसे अधिक स्पष्टीकरण मिला है कि मैं काफी दुर्गम हो गया हूं और व्यावहारिक विस्तार में कमी है।
हाल ही में हास्केल को संक्षेप में देखने पर, एक संक्षिप्त, संक्षिप्त, व्यावहारिक स्पष्टीकरण क्या होगा जो अनिवार्य रूप से एक सनक है?
मुझे सबसे अधिक स्पष्टीकरण मिला है कि मैं काफी दुर्गम हो गया हूं और व्यावहारिक विस्तार में कमी है।
जवाबों:
पहला: अगर आप गणितज्ञ नहीं हैं, तो मोनड शब्द थोड़ा खाली है। एक वैकल्पिक शब्द अभिकलन बिल्डर है जो वास्तव में उनके लिए उपयोगी होने का थोड़ा अधिक विवरणात्मक है।
आप व्यावहारिक उदाहरण के लिए पूछें:
उदाहरण 1: सूची समझ :
[x*2 | x<-[1..10], odd x]
यह अभिव्यक्ति 1 से 10. तक की सभी विषम संख्याओं के युगल को बहुत उपयोगी बताती है!
यह पता चला है कि यह सूची मोनाड के भीतर कुछ कार्यों के लिए वास्तव में सिर्फ वाक्यात्मक चीनी है। एक ही सूची समझ के रूप में लिखा जा सकता है:
do
x <- [1..10]
guard (odd x)
return (x * 2)
या और भी:
[1..10] >>= (\x -> guard (odd x) >> return (x*2))
उदाहरण 2: इनपुट / आउटपुट :
do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Welcome, " ++ name ++ "!")
दोनों उदाहरणों में मोनाड्स, एकेए कम्प्यूटेशन बिल्डरों का उपयोग किया गया है। सामान्य विषय यह है कि कुछ विशिष्ट, उपयोगी तरीके से मोनाड चेन का संचालन होता है। सूची की समझ में, संचालन ऐसे जंजीरों में जकड़े हुए हैं कि यदि कोई ऑपरेशन सूची देता है, तो सूची में प्रत्येक आइटम पर निम्नलिखित संचालन किए जाते हैं । दूसरी ओर IO मोनाड क्रमिक रूप से संचालन करता है, लेकिन साथ में एक "छिपा हुआ चर" गुजरता है, जो "दुनिया की स्थिति" का प्रतिनिधित्व करता है, जो हमें शुद्ध कार्यात्मक तरीके से I / O कोड लिखने की अनुमति देता है।
यह पता चला है कि चेनिंग ऑपरेशन का पैटर्न काफी उपयोगी है और इसका उपयोग हास्केल में बहुत सी विभिन्न चीजों के लिए किया जाता है।
एक अन्य उदाहरण अपवाद हैं: Error
मोनाड का उपयोग करते हुए , संचालन को ऐसे जंजीरों से जकड़ा जाता है कि उन्हें क्रमिक रूप से निष्पादित किया जाता है, सिवाय इसके कि यदि कोई त्रुटि है, तो उस स्थिति में बाकी श्रृंखला को छोड़ दिया जाता है।
दोनों सूची-बोध वाक्यविन्यास और >>=
संचालक का उपयोग करते हुए परिचालनों का संचालन करने के लिए संकेतन चीनी हैं । एक मोनाड मूल रूप से केवल एक प्रकार है जो >>=
ऑपरेटर का समर्थन करता है ।
उदाहरण 3: एक तोता
यह एक बहुत ही सरल पार्सर है जो या तो एक उद्धृत स्ट्रिंग या संख्या को पार करता है:
parseExpr = parseString <|> parseNumber
parseString = do
char '"'
x <- many (noneOf "\"")
char '"'
return (StringValue x)
parseNumber = do
num <- many1 digit
return (NumberValue (read num))
संचालन char
, digit
आदि बहुत सरल कर रहे हैं। वे या तो मेल खाते हैं या मेल नहीं खाते। जादू एक ऐसा सन्यासी है जो नियंत्रण प्रवाह का प्रबंधन करता है: संचालन क्रमिक रूप से तब तक किया जाता है जब तक कि कोई मैच विफल न हो जाए, इस स्थिति में मोनाड नवीनतम पर वापस आ जाता है <|>
और अगले विकल्प की कोशिश करता है। फिर, कुछ अतिरिक्त, उपयोगी शब्दार्थों के साथ संचालन का एक तरीका।
उदाहरण 4: अतुल्यकालिक प्रोग्रामिंग
उपरोक्त उदाहरण हास्केल में हैं, लेकिन यह F # को भी मोनाड्स का समर्थन करता है। यह उदाहरण डॉन सिम से चुराया गया है :
let AsyncHttp(url:string) =
async { let req = WebRequest.Create(url)
let! rsp = req.GetResponseAsync()
use stream = rsp.GetResponseStream()
use reader = new System.IO.StreamReader(stream)
return reader.ReadToEnd() }
यह विधि एक वेब पेज लाती है। पंच लाइन का उपयोग होता है GetResponseAsync
- यह वास्तव में एक अलग धागे पर प्रतिक्रिया की प्रतीक्षा करता है, जबकि मुख्य धागा फ़ंक्शन से लौटता है। प्रतिक्रिया मिलने पर पिछली तीन पंक्तियों को स्पॉन्ड थ्रेड पर निष्पादित किया जाता है।
अधिकांश अन्य भाषाओं में आपको स्पष्ट रूप से प्रतिक्रिया को संभालने वाली लाइनों के लिए एक अलग फ़ंक्शन बनाना होगा। async
इकाई "विभाजन" करने के लिए अपने आप ही ब्लॉक में सक्षम है और उत्तरार्ध के निष्पादन को स्थगित। ( async {}
सिंटैक्स इंगित करता है कि ब्लॉक में नियंत्रण प्रवाह को async
मोनाड द्वारा परिभाषित किया गया है ।)
वे कैसे काम करते हैं
तो एक सन्यासी इन सभी फैंसी नियंत्रण-प्रवाह को कैसे कर सकता है? वास्तव में एक डो-ब्लॉक (या एक गणना अभिव्यक्ति के रूप में वे एफ # में कहा जाता है) में होता है, यह है कि प्रत्येक ऑपरेशन (मूल रूप से प्रत्येक पंक्ति) एक अलग अनाम फ़ंक्शन में लपेटा जाता है। इन कार्यों को तब bind
ऑपरेटर ( >>=
हास्केल में वर्तनी ) का उपयोग करके संयोजित किया जाता है । चूंकि bind
ऑपरेशन कार्यों को जोड़ती है, इसलिए यह उन्हें निष्पादित कर सकता है क्योंकि यह फिट देखता है: क्रमिक रूप से, कई बार, रिवर्स में, कुछ को त्यागें, कुछ को एक अलग थ्रेड पर निष्पादित करें जब यह ऐसा लगता है और इसी तरह।
एक उदाहरण के रूप में, यह उदाहरण 2 से IO- कोड का विस्तारित संस्करण है:
putStrLn "What is your name?"
>>= (\_ -> getLine)
>>= (\name -> putStrLn ("Welcome, " ++ name ++ "!"))
यह बदसूरत है, लेकिन यह और भी स्पष्ट है कि वास्तव में क्या चल रहा है। >>=
ऑपरेटर जादू घटक है: यह (दाईं ओर) एक समारोह के साथ (बाईं ओर) एक मूल्य है और यह जोड़ती है लेता है, एक नया मान उत्पादन करने के लिए। यह नया मान फिर अगले >>=
ऑपरेटर द्वारा लिया जाता है और फिर से एक नए मूल्य का उत्पादन करने के लिए एक फ़ंक्शन के साथ जोड़ा जाता है। >>=
एक मिनी-मूल्यांकनकर्ता के रूप में देखा जा सकता है।
ध्यान दें कि >>=
विभिन्न प्रकारों के लिए अतिभारित है, इसलिए प्रत्येक मोनाड का अपना कार्यान्वयन है >>=
। (श्रृंखला के सभी ऑपरेशनों को हालांकि एक ही मोनाड के प्रकार का होना चाहिए, अन्यथा >>=
ऑपरेटर काम नहीं करेगा।)
बस का सरलतम संभव कार्यान्वयन >>=
बाईं ओर मान लेता है और इसे दाईं ओर फ़ंक्शन पर लागू करता है और परिणाम लौटाता है, लेकिन जैसा कि पहले कहा गया है, जो पूरे पैटर्न को उपयोगी बनाता है वह यह है कि मोनाद के कार्यान्वयन में कुछ अतिरिक्त चल रहा है >>=
।
एक ऑपरेशन से दूसरे ऑपरेशन में मूल्यों को कैसे पारित किया जाता है, इसमें कुछ अतिरिक्त चतुराई है, लेकिन इसके लिए हास्केल प्रकार प्रणाली की गहन व्याख्या की आवश्यकता है।
उपसंहार
हास्केल-शब्दों में एक मोनाड एक मानकीकृत प्रकार है जो मोनाड प्रकार के वर्ग का एक उदाहरण है, जो >>=
कुछ अन्य ऑपरेटरों के साथ परिभाषित करता है । आम आदमी की शर्तों में, एक मोनाड सिर्फ एक प्रकार है जिसके लिए >>=
ऑपरेशन को परिभाषित किया जाता है।
अपने आप >>=
में चाइनिंग फंक्शंस का एक बोझिल तरीका है, लेकिन "प्लंबिंग" को छिपाने वाली डू-नोटेशन की उपस्थिति के साथ, मोनैडिक ऑपरेशंस बहुत अच्छा और उपयोगी एब्सट्रैक्शन, भाषा में उपयोगी कई जगह, और उपयोगी हो जाता है। भाषा में अपनी लघु-भाषा बनाने के लिए।
भिक्षु कठिन क्यों होते हैं?
कई हास्केल-शिक्षार्थियों के लिए, मोनड्स एक बाधा है जिसे वे ईंट की दीवार की तरह मारते हैं। ऐसा नहीं है कि मोनैड्स स्वयं जटिल हैं, लेकिन यह कि कार्यान्वयन कई अन्य उन्नत हास्केल सुविधाओं पर निर्भर करता है जैसे कि मानकीकृत प्रकार, प्रकार की कक्षाएं, और इसी तरह। समस्या यह है कि हास्केल I / O भिक्षुओं पर आधारित है, और I / O संभवत: पहली चीजों में से एक है जिसे आप एक नई भाषा सीखते समय समझना चाहते हैं - आखिरकार, यह उन कार्यक्रमों को बनाने के लिए बहुत मजेदार नहीं है जो किसी भी चीज का उत्पादन नहीं करते हैं उत्पादन। मेरे पास इस चिकन और अंडे की समस्या का कोई तत्काल समाधान नहीं है, सिवाय इसके कि मैं / ओ जैसा "जादू यहां होता है" जब तक आपके पास भाषा के अन्य हिस्सों के साथ पर्याप्त अनुभव न हो। माफ़ करना।
भिक्षुओं पर उत्कृष्ट ब्लॉग: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
"क्या एक संन्यासी है" यह बताते हुए कि "संख्या क्या है?" हम हर समय संख्याओं का उपयोग करते हैं। लेकिन कल्पना कीजिए कि आप किसी ऐसे व्यक्ति से मिले जो संख्याओं के बारे में कुछ नहीं जानता था। कैसे बिल्ली आप की व्याख्या करता है क्या नंबर दिए गए हैं? और आप यह भी वर्णन करना शुरू करेंगे कि यह उपयोगी क्यों हो सकता है?
सन्यासी क्या होता है? संक्षिप्त उत्तर: यह एक साथ परिचालन संचालन का एक विशिष्ट तरीका है।
संक्षेप में, आप निष्पादन चरणों को लिख रहे हैं और उन्हें "बाइंड फ़ंक्शन" के साथ जोड़ रहे हैं। (हास्केल में, इसका नाम है >>=
।) आप बाइंड ऑपरेटर को कॉल लिख सकते हैं, या आप सिंटेक्स शुगर का उपयोग कर सकते हैं जो कंपाइलर को आपके लिए उन फ़ंक्शन कॉल को सम्मिलित करने की अनुमति देता है। लेकिन किसी भी तरह से, प्रत्येक कदम इस बाइंड फ़ंक्शन के लिए एक कॉल द्वारा अलग किया जाता है।
तो बाइंड फ़ंक्शन एक अर्धविराम की तरह है; यह एक प्रक्रिया में चरणों को अलग करता है। बाइंड फ़ंक्शन का काम आउटपुट को पिछले चरण से लेना है, और इसे अगले चरण में फीड करना है।
यह बहुत मुश्किल ध्वनि नहीं है, है ना? लेकिन एक से अधिक प्रकार के मोनाड हैं। क्यों? कैसे?
खैर, बाइंड फ़ंक्शन केवल एक कदम से परिणाम ले सकता है , और इसे अगले चरण में खिला सकता है। लेकिन अगर वह सब "मोनाड" करता है ... जो वास्तव में बहुत उपयोगी नहीं है। और यह समझना महत्वपूर्ण है: हर उपयोगी सन्यासी सिर्फ एक सन्यासी होने के अलावा कुछ और करता है । प्रत्येक उपयोगी मोनाड में एक "विशेष शक्ति" होती है, जो इसे अद्वितीय बनाती है।
(एक ऐसा सन्यासी, जो कुछ खास नहीं करता है, उसे "पहचान सन्यासी" कहा जाता है। पहचान समारोह की तरह, यह पूरी तरह से निरर्थक बात की तरह लगता है, फिर भी पता नहीं चलता है ... लेकिन यह एक और कहानी ™ है।)
मूल रूप से, प्रत्येक संन्यासी का बाइंड फ़ंक्शन का अपना कार्यान्वयन है। और आप एक बाइंड फ़ंक्शन लिख सकते हैं जैसे कि यह निष्पादन चरणों के बीच हुपली चीजें करता है। उदाहरण के लिए:
यदि प्रत्येक चरण एक सफलता / विफलता संकेतक देता है, तो आप अगले चरण को केवल तभी निष्पादित कर सकते हैं जब पिछले वाला सफल हो गया हो। इस तरह, एक असफल कदम पूरे अनुक्रम को "स्वचालित रूप से" समाप्त कर देता है, बिना किसी सशर्त परीक्षण के। ( विफलता मोनाड ।)
इस विचार को विस्तारित करते हुए, आप "अपवाद" को लागू कर सकते हैं। ( त्रुटि मोनाड या अपवाद मोनाड ।) क्योंकि आप उन्हें भाषा सुविधा होने के बजाय स्वयं परिभाषित कर रहे हैं, आप यह परिभाषित कर सकते हैं कि वे कैसे काम करते हैं। (उदाहरण के लिए, हो सकता है कि आप पहले दो अपवादों को नजरअंदाज करना चाहते हों और तीसरा अपवाद फेंकने पर केवल गर्भपात ही करना चाहते हों ।)
आप प्रत्येक चरण को कई परिणाम दे सकते हैं , और उन पर बाँध फ़ंक्शन लूप रख सकते हैं, जो प्रत्येक को आपके लिए अगले चरण में खिलाते हैं। इस प्रकार, आपको कई परिणामों के साथ काम करते समय सभी जगह लूप लिखना नहीं पड़ता है। बाइंड फ़ंक्शन "स्वचालित रूप से" आपके लिए वह सब करता है। ( सूची मोनद ।)
एक चरण से दूसरे चरण में "परिणाम" पास करने के साथ-साथ, आप बाँध फ़ंक्शन पास के अतिरिक्त डेटा को भी पास कर सकते हैं । यह डेटा अब आपके स्रोत कोड में दिखाई नहीं देता है, लेकिन आप अभी भी इसे कहीं से भी एक्सेस कर सकते हैं, बिना इसे मैन्युअल रूप से हर फ़ंक्शन को पास करने के लिए। ( पाठक मोनाद ।)
आप इसे बना सकते हैं ताकि "अतिरिक्त डेटा" को बदला जा सके। यह आपको विनाशकारी अपडेट का अनुकरण करने की अनुमति देता है , वास्तव में विनाशकारी अपडेट किए बिना। ( स्टेट मोनाड और इसके चचेरे भाई लेखक मोनाड ।)
क्योंकि आप केवल विनाशकारी अद्यतनों का अनुकरण कर रहे हैं , आप वास्तविक विनाशकारी अद्यतनों के साथ असंभव काम कर सकते हैं । उदाहरण के लिए, आप पिछले अपडेट को पूर्ववत कर सकते हैं , या पुराने संस्करण में वापस ला सकते हैं ।
आप एक मोनाड बना सकते हैं जहां गणना को रोका जा सकता है , इसलिए आप अपने कार्यक्रम को रोक सकते हैं, आंतरिक स्थिति डेटा के साथ अंदर जा सकते हैं और फिर इसे फिर से शुरू कर सकते हैं।
आप एक मठ के रूप में "निरंतरता" को लागू कर सकते हैं। यह आपको लोगों के दिमाग को तोड़ने की अनुमति देता है !
यह और सब कुछ मठों के साथ संभव है। बेशक, यह सब बिना मोनाड के भी पूरी तरह से संभव है। भिक्षुओं का उपयोग करना बहुत आसान है ।
दरअसल, मोनाड्स की आम समझ के विपरीत, उनका राज्य से कोई लेना-देना नहीं है। मोनाड बस चीजों को लपेटने का एक तरीका है और बिना लिपटे इसे लपेटे हुए सामान पर संचालन करने के तरीके प्रदान करते हैं।
उदाहरण के लिए, आप हास्केल में एक दूसरे को लपेटने के लिए एक प्रकार बना सकते हैं:
data Wrapped a = Wrap a
सामान लपेटने के लिए हम परिभाषित करते हैं
return :: a -> Wrapped a
return x = Wrap x
बिना किसी ऑपरेशन के प्रदर्शन करने के लिए, कहें कि आपके पास एक फ़ंक्शन है f :: a -> b
, तो आप ऐसा कर सकते हैं कि लिपटे मूल्यों पर कार्य करने के लिए उस फ़ंक्शन को उठाएं :
fmap :: (a -> b) -> (Wrapped a -> Wrapped b)
fmap f (Wrap x) = Wrap (f x)
बस इतना ही समझना है। हालांकि, यह पता चला है कि इस उठाने के लिए एक अधिक सामान्य कार्य है , जो है bind
:
bind :: (a -> Wrapped b) -> (Wrapped a -> Wrapped b)
bind f (Wrap x) = f x
bind
से थोड़ा अधिक कर सकते हैं fmap
, लेकिन इसके विपरीत नहीं। वास्तव में, fmap
केवल के रूप में परिभाषित किया जा सकता है bind
और return
। इसलिए, जब एक साधु को परिभाषित करते हैं .. तो आप इसके प्रकार (यहां यह था Wrapped a
) देते हैं और फिर कहते हैं कि इसके return
और bind
संचालन कैसे काम करते हैं।
अच्छी बात यह है कि यह एक सामान्य पैटर्न है कि यह सभी जगह पर पॉप अप होता है, शुद्ध तरीके से राज्य को घेरना केवल उनमें से एक है।
कार्यात्मक निर्भरता का परिचय देने के लिए और इस तरह मूल्यांकन के नियंत्रण आदेश का उपयोग कैसे किया जा सकता है, इस पर एक अच्छे लेख के लिए, जैसे कि हास्केल के आईओ मोनड में इसका उपयोग किया जाता है, आईओ इनसाइड देखें ।
भिक्षुओं को समझने के लिए, इसके बारे में बहुत ज्यादा चिंता न करें। उनके बारे में पढ़ें जो आपको दिलचस्प लगता है और चिंता न करें अगर आप अभी नहीं समझते हैं। तब हास्केल जैसी भाषा में सिर्फ डाइविंग करना ही रास्ता है। मोनाड इन चीजों में से एक है जहां अभ्यास द्वारा आपके मस्तिष्क में चाल को समझना, एक दिन जब आप अचानक महसूस करते हैं कि आप उन्हें समझते हैं।
लेकिन, आप मोनाड्स का आविष्कार कर सकते थे!
सिगफ़ेप कहता है:
लेकिन ये सभी स्पष्टीकरण की आवश्यकता में कुछ गूढ़ के रूप में मठों का परिचय देते हैं। लेकिन मैं जो तर्क देना चाहता हूं वह यह है कि वे गूढ़ नहीं हैं। वास्तव में, कार्यात्मक प्रोग्रामिंग में आपको विभिन्न समस्याओं का सामना करना पड़ा है, जो निश्चित रूप से कुछ समाधानों के लिए नेतृत्व किया गया है, जिनमें से सभी मठों के उदाहरण हैं। वास्तव में, मुझे आशा है कि यदि आप पहले से ही नहीं हैं, तो अब आप उन्हें आविष्कार करने के लिए प्राप्त करेंगे। यह तब नोटिस करने के लिए एक छोटा सा कदम है कि ये सभी समाधान वास्तव में भेस में एक ही समाधान हैं। और इसे पढ़ने के बाद, आप भिक्षुओं के अन्य दस्तावेजों को समझने के लिए बेहतर स्थिति में हो सकते हैं क्योंकि आप वह सब कुछ पहचान लेंगे जो आप पहले से आविष्कार किए गए कुछ के रूप में देखते हैं।
जिन समस्याओं को हल करने की कोशिश करते हैं उनमें से कई साइड इफेक्ट के मुद्दे से संबंधित हैं। तो हम उनके साथ शुरू करेंगे। (ध्यान दें कि भिक्षु आपको साइड-इफ़ेक्ट से अधिक कुछ करने देते हैं, विशेष रूप से कई प्रकार के कंटेनर ऑब्जेक्ट को मोनाड्स के रूप में देखा जा सकता है। मोनाड्स के कुछ परिचय, इन दोनों साधुओं के अलग-अलग उपयोगों को समेटने में मुश्किल पाते हैं और बस ध्यान केंद्रित करते हैं। अन्य।)
C ++ जैसी अनिवार्य प्रोग्रामिंग भाषा में, फ़ंक्शन गणित के कार्यों की तरह कुछ भी व्यवहार नहीं करते हैं। उदाहरण के लिए, मान लें कि हमारे पास एक C ++ फ़ंक्शन है जो एक एकल फ़्लोटिंग पॉइंट तर्क लेता है और एक फ़्लोटिंग पॉइंट परिणाम देता है। सतही तौर पर यह एक गणितीय फ़ंक्शन की तरह प्रतीत होता है जो वास्तविक से वास्तविक मानचित्रण करता है, लेकिन C ++ फ़ंक्शन केवल एक संख्या को वापस करने से अधिक कर सकता है जो इसके तर्कों पर निर्भर करता है। यह स्क्रीन पर आउटपुट लिखने और उपयोगकर्ता से इनपुट प्राप्त करने के साथ-साथ वैश्विक चर के मूल्यों को पढ़ और लिख सकता है। एक शुद्ध कार्यात्मक भाषा में, हालांकि, एक फ़ंक्शन केवल वही पढ़ सकता है जो उसे अपने तर्कों में आपूर्ति करता है और जिस तरह से यह दुनिया पर प्रभाव डाल सकता है, वह केवल उन मूल्यों के माध्यम से है जो इसे वापस करता है।
एक मोनाड एक डेटाटाइप है जिसमें दो ऑपरेशन होते हैं: >>=
(उर्फ bind
) और return
(उर्फ unit
)। return
एक मनमाना मूल्य लेता है और इसके साथ मठ का एक उदाहरण बनाता है। >>=
मोनाड का एक उदाहरण लेता है और इस पर एक फ़ंक्शन मैप करता है। (आप पहले से ही देख सकते हैं कि एक मोनाड एक अजीब प्रकार का डेटाटाइप है, क्योंकि अधिकांश प्रोग्रामिंग भाषाओं में आप एक फ़ंक्शन नहीं लिख सकते हैं जो एक मनमाना मूल्य लेता है और इससे एक प्रकार का निर्माण करता है। मोनाड्स एक प्रकार के पैरामीट्रिक बहुरूपता का उपयोग करते हैं ।)
हास्केल संकेतन में, मोनाड इंटरफ़ेस लिखा गया है
class Monad m where
return :: a -> m a
(>>=) :: forall a b . m a -> (a -> m b) -> m b
ये संचालन कुछ "कानूनों" का पालन करने वाले हैं, लेकिन यह बहुत महत्वपूर्ण नहीं है: "कानून" जिस तरह से व्यवहार करने के लिए आवश्यक संचालन के समझदार कार्यान्वयन को कोडित करते हैं (मूल रूप से, कि >>=
और return
इस बात से सहमत होना चाहिए कि कैसे मानद उदाहरणों में रूपांतरित हो जाते हैं और वह >>=
सहयोगी है)।
मोनाड केवल राज्य और I / O के बारे में नहीं हैं: वे गणना के एक सामान्य पैटर्न को सार करते हैं जिसमें राज्य, I / O, अपवाद और गैर-नियतत्ववाद के साथ काम करना शामिल है। संभवतः समझने के लिए सबसे सरल मठ हैं सूचियां और विकल्प प्रकार:
instance Monad [ ] where
[] >>= k = []
(x:xs) >>= k = k x ++ (xs >>= k)
return x = [x]
instance Monad Maybe where
Just x >>= k = k x
Nothing >>= k = Nothing
return x = Just x
जहां []
और :
सूची निर्माता ये हैं, ++
संयोजन ऑपरेटर है, और Just
और Nothing
कर रहे हैं Maybe
कंस्ट्रक्टर्स। ये दोनों मोनाड अपने संबंधित डेटा प्रकारों पर गणना के सामान्य और उपयोगी पैटर्न को नोट करते हैं (ध्यान दें कि न तो साइड इफेक्ट्स या आई / ओ के साथ कुछ करना है)।
आपको वास्तव में कुछ गैर-तुच्छ हास्केल कोड लिखने के लिए खेलना होगा, जो इस बात की सराहना करते हैं कि भिक्षुओं के बारे में क्या है और वे क्यों उपयोगी हैं।
आपको सबसे पहले समझना चाहिए कि एक फनकार क्या है। इससे पहले, उच्च-क्रम के कार्यों को समझें।
एक उच्च-क्रम फ़ंक्शन केवल एक फ़ंक्शन है जो फ़ंक्शन को तर्क के रूप में लेता है।
एक फ़ंक्टर किसी भी प्रकार का निर्माण T
है जिसके लिए एक उच्च-क्रम फ़ंक्शन मौजूद है, इसे कॉल करें map
, जो फ़ंक्शन के प्रकार a -> b
(किसी भी दो प्रकार ) a
और b
एक फ़ंक्शन में बदल देता है T a -> T b
। इस map
फ़ंक्शन को भी पहचान और संरचना के नियमों का पालन करना चाहिए ताकि निम्नलिखित अभिव्यक्तियाँ सभी के लिए सही हों p
और q
(हास्केल संकेतन):
map id = id
map (p . q) = map p . map q
उदाहरण के लिए, एक प्रकार का कंस्ट्रक्टर जिसे कहा जाता है List
वह एक फ़नकार है यदि यह एक प्रकार के फ़ंक्शन से सुसज्जित है (a -> b) -> List a -> List b
जो उपरोक्त कानूनों का पालन करता है। केवल व्यावहारिक कार्यान्वयन स्पष्ट है। परिणामी List a -> List b
फ़ंक्शन दी गई सूची पर निर्भर (a -> b)
करता है, प्रत्येक तत्व के लिए फ़ंक्शन को बुलाता है , और परिणामों की सूची लौटाता है।
एक इकाई मात्र हैं एक functor है T
दो अतिरिक्त तरीकों के साथ, join
, प्रकार की T (T a) -> T a
है, और unit
(कभी कभी कहा जाता है return
, fork
या pure
प्रकार के) a -> T a
। हास्केल में सूची के लिए:
join :: [[a]] -> [a]
pure :: a -> [a]
क्यों उपयोगी है? क्योंकि आप, उदाहरण के लिए, map
एक सूची के साथ एक सूची में जो एक सूची देता है। Join
सूचियों की परिणामी सूची लेता है और उन्हें संक्षिप्त करता है। List
एक भिक्षु है क्योंकि यह संभव है।
आप एक फ़ंक्शन लिख सकते हैं जो map
तब करता है join
। इस फ़ंक्शन को कहा जाता है bind
, या flatMap
, या (>>=)
, या (=<<)
। यह आम तौर पर हास्केल में एक मोनड उदाहरण दिया जाता है।
एक संन्यासी को कुछ कानूनों को संतुष्ट join
करना पड़ता है , अर्थात् साहचर्य होना चाहिए। इसका मतलब है कि यदि आपके पास x
प्रकार का मूल्य है [[[a]]]
तो join (join x)
बराबर होना चाहिए join (map join x)
। और इस तरह के pure
लिए एक पहचान होनी चाहिए ।join
join (pure x) == x
[अस्वीकरण: मैं अभी भी पूरी तरह से भिक्षुओं को ग्रसने की कोशिश कर रहा हूं। निम्नलिखित सिर्फ वही है जो मैंने अब तक समझा है। अगर यह गलत है, तो उम्मीद है कि कोई जानकार मुझे कालीन पर बुलाएगा।]
अरनार ने लिखा:
मोनाड बस चीजों को लपेटने का एक तरीका है और बिना लिपटे इसे लपेटे गए सामान पर संचालन करने के तरीके प्रदान करते हैं।
ठीक यही है। विचार इस प्रकार है:
आप किसी तरह का मूल्य लेते हैं और इसे कुछ अतिरिक्त जानकारी के साथ लपेटते हैं। जैसे मान एक निश्चित प्रकार का होता है (जैसे। पूर्णांक या स्ट्रिंग), तो अतिरिक्त जानकारी एक निश्चित प्रकार की होती है।
उदाहरण के लिए, अतिरिक्त जानकारी एक Maybe
या एक हो सकती है IO
।
फिर आपके पास कुछ ऑपरेटर हैं जो आपको उस अतिरिक्त जानकारी के साथ ले जाने के दौरान लिपटे डेटा पर काम करने की अनुमति देते हैं। ये ऑपरेटर अतिरिक्त जानकारी का उपयोग यह तय करने के लिए करते हैं कि लिपटे मूल्य पर ऑपरेशन के व्यवहार को कैसे बदलना है।
जैसे, Maybe Int
a Just Int
या हो सकता है Nothing
। अब, यदि आप एक Maybe Int
को जोड़ते हैं Maybe Int
, तो ऑपरेटर यह देखने के लिए जाँच करेगा कि क्या वे दोनों Just Int
अंदर हैं, और यदि ऐसा है, तो Int
एस को अनफ्रेंड कर देंगे , उन्हें अतिरिक्त ऑपरेटर पास कर देंगे, परिणामस्वरूप रि-रैप Int
को एक नया Just Int
(जो एक मान्य हो) Maybe Int
), और इस प्रकार वापसी Maybe Int
। लेकिन अगर उनमें से एक Nothing
अंदर था , तो यह ऑपरेटर बस तुरंत वापस आ जाएगा Nothing
, जो फिर से वैध है Maybe Int
। इस तरह, आप यह दिखावा कर सकते हैं कि आपके Maybe Int
s सिर्फ सामान्य संख्या हैं और उन पर नियमित गणित करते हैं। यदि आप एक पाने के लिए थे Nothing
, तो आपके समीकरण अभी भी सही परिणाम उत्पन्न करेंगे - आपके बिना Nothing
हर जगह की जांच करने के लिए ।
लेकिन उदाहरण सिर्फ इसके लिए क्या होता है Maybe
। यदि अतिरिक्त जानकारी एक थी IO
, तो उस विशेष ऑपरेटर को IO
एस के लिए परिभाषित किया जाएगा, इसके बजाय बुलाया जाएगा, और यह अतिरिक्त प्रदर्शन करने से पहले पूरी तरह से कुछ अलग कर सकता है। (ठीक है, दो IO Int
एस को एक साथ जोड़ना संभवतः निरर्थक है - मुझे अभी तक यकीन नहीं है।) (इसके अलावा, यदि आपने Maybe
उदाहरण पर ध्यान दिया है, तो आपने देखा है कि "अतिरिक्त सामान के साथ एक मूल्य लपेटना" हमेशा सही नहीं होता है। लेकिन यह कठिन है। अयोग्य होने के बिना सटीक, सही और सटीक होना।)
मूल रूप से, "सनक" का अर्थ मोटे तौर पर "पैटर्न" होता है । लेकिन अनौपचारिक रूप से समझाई गई और विशेष रूप से नामित पैटर्नों से भरी किताब के बजाय, अब आपके पास एक भाषा निर्माण है - वाक्यविन्यास और सभी - जो आपको अपने कार्यक्रम में नए पैटर्न घोषित करने की अनुमति देता है । (यहाँ खराबी सभी पैटर्न को एक विशेष रूप का पालन करना है, इसलिए एक सनक एक पैटर्न के रूप में सामान्य नहीं है। लेकिन मुझे लगता है कि यह निकटतम शब्द है जिसे ज्यादातर लोग जानते हैं और समझते हैं।)
और इसीलिए लोग भिक्षुओं को इतना भ्रामक पाते हैं: क्योंकि वे एक ऐसी सामान्य अवधारणा हैं। यह पूछने के लिए कि कोई चीज क्या बनाती है, यह वैसा ही अस्पष्ट है जैसा कि यह पूछना कि कोई चीज क्या पैटर्न बनाती है।
लेकिन एक पैटर्न के विचार के लिए भाषा में वाक्यविन्यास समर्थन होने के निहितार्थ के बारे में सोचें: चार किताबों के गैंग को पढ़ने और एक विशेष पैटर्न के निर्माण को याद करने के बजाय, आप बस कोड लिखते हैं जो इस पैटर्न को अज्ञेयवाद में लागू करता है। सामान्य तरीका एक बार और फिर आप कर रहे हैं! आप इस पैटर्न का पुन: उपयोग कर सकते हैं, जैसे आगंतुक या रणनीति या फ़ेकडे या जो भी हो, बस इसके साथ अपने कोड में संचालन को सजाने के बिना, इसे फिर से लागू करने के बिना!
यही कारण है कि जो लोग भिक्षुओं को समझते हैं, उन्हें इतना उपयोगी लगता है : यह कुछ हाथी दांत टॉवर अवधारणा नहीं है जो बौद्धिक स्नोबॉल खुद को समझने पर गर्व करते हैं (ठीक है, वह भी बेशक, टीह), लेकिन वास्तव में कोड को सरल बनाता है।
M (M a) -> M a
। यह तथ्य कि आप इसे एक प्रकार में बदल सकते हैं, जो M a -> (a -> M b) -> M b
उन्हें उपयोगी बनाता है।
बहुत प्रयास करने के बाद, मुझे लगता है कि मैं अंत में भिक्षु को समझता हूं। भारी मतदान के अपने स्वयं के लम्बे समालोचकों के पुनर्मिलन के बाद, मैं इस स्पष्टीकरण की पेशकश करूंगा।
तीन सवाल हैं जिनका जवाब साधुओं को समझने के लिए दिया जाना चाहिए:
जैसा कि मैंने अपनी मूल टिप्पणियों में उल्लेख किया है, बहुत सारे सनक स्पष्टीकरण प्रश्न संख्या 3 में पकड़े जाते हैं, बिना, और वास्तव में पर्याप्त रूप से प्रश्न 2, या प्रश्न 1 को कवर करने से पहले।
आपको एक सन्यासी की आवश्यकता क्यों है?
हास्केल जैसी शुद्ध कार्यात्मक भाषाएं सी, या जावा जैसी अनिवार्य भाषाओं से अलग हैं, एक विशिष्ट कार्यात्मक कार्यक्रम को एक विशिष्ट क्रम में निष्पादित नहीं किया जाता है, एक समय में एक कदम। एक हास्केल कार्यक्रम गणितीय फ़ंक्शन के लिए अधिक महत्वपूर्ण है, जिसमें आप किसी भी संभावित आदेशों में "समीकरण" को हल कर सकते हैं। इससे कई लाभ मिलते हैं, जिसके बीच यह कुछ प्रकार के कीड़े, विशेषकर "राज्य" जैसी चीजों से संबंधित संभावना को समाप्त करता है।
हालांकि, कुछ समस्याएं हैं जो प्रोग्रामिंग की इस शैली के साथ हल करने के लिए इतनी सीधी नहीं हैं। कंसोल प्रोग्रामिंग, और फ़ाइल i / o जैसी कुछ चीजें, एक विशेष क्रम में होने वाली चीजों की आवश्यकता होती हैं, या राज्य बनाए रखने की आवश्यकता होती हैं। इस समस्या से निपटने का एक तरीका यह है कि एक प्रकार की वस्तु बनाई जाए जो एक संगणना की स्थिति का प्रतिनिधित्व करती है, और कई प्रकार के कार्य जो राज्य वस्तु को इनपुट के रूप में लेते हैं, और एक नई संशोधित राज्य वस्तु वापस करते हैं।
तो चलो एक काल्पनिक "राज्य" मान बनाते हैं, जो कंसोल स्क्रीन की स्थिति का प्रतिनिधित्व करता है। वास्तव में इस मूल्य का निर्माण कैसे किया जाता है यह महत्वपूर्ण नहीं है, लेकिन मान लें कि यह बाइट की लंबाई वाली एसेसी वर्णों की एक सरणी है जो वर्तमान में स्क्रीन पर दिखाई दे रहा है, और एक सरणी जो उपयोगकर्ता द्वारा दर्ज किए गए इनपुट की अंतिम पंक्ति का प्रतिनिधित्व करता है, pseudocode में। हमने कुछ कार्य परिभाषित किए हैं जो कंसोल स्टेट लेते हैं, इसे संशोधित करते हैं और एक नया कंसोल स्टेट लौटाते हैं।
consolestate MyConsole = new consolestate;
तो कंसोल प्रोग्रामिंग करने के लिए, लेकिन शुद्ध कार्यात्मक तरीके से, आपको प्रत्येक अभिभावक के अंदर बहुत सारे फ़ंक्शन कॉल करने की आवश्यकता होगी।
consolestate FinalConsole = print(input(print(myconsole, "Hello, what's your name?")),"hello, %inputbuffer%!");
इस तरह से प्रोग्रामिंग करने से "शुद्ध" कार्यात्मक शैली बनी रहती है, जबकि कंसोल में परिवर्तन एक विशेष क्रम में होने के लिए मजबूर करता है। लेकिन, हम शायद एक बार में केवल कुछ ऑपरेशनों से अधिक करना चाहते हैं, जैसे ऊपर दिए गए उदाहरण में। उस तरह से घोंसले के कार्य अनजाने में होने लगेंगे। हम जो चाहते हैं, वह कोड है जो अनिवार्य रूप से ऊपर के समान काम करता है, लेकिन इस तरह से थोड़ा अधिक लिखा जाता है:
consolestate FinalConsole = myconsole:
print("Hello, what's your name?"):
input():
print("hello, %inputbuffer%!");
यह वास्तव में इसे लिखने का एक अधिक सुविधाजनक तरीका होगा। हालांकि हम ऐसा कैसे करते हैं?
सन्यासी क्या होता है?
एक बार जब आपके पास एक प्रकार (जैसे कि consolestate
) होता है, जिसे आप विशेष रूप से उस प्रकार पर काम करने के लिए डिज़ाइन किए गए कार्यों के एक समूह के साथ परिभाषित करते हैं, तो आप इन चीजों के पूरे पैकेज को "मोनड" में बदल सकते हैं, जैसे ऑपरेटर :
(बाँध) को स्वचालित रूप से परिभाषित करके फ़ीड मानों को इसके बाईं ओर, फ़ंक्शन पैरामीटर में इसके दाईं ओर, और एक lift
ऑपरेटर जो सामान्य कार्यों को चालू करता है, उस विशिष्ट प्रकार के बाइंड ऑपरेटर के साथ काम करता है।
एक मोनाड को कैसे लागू किया जाता है?
अन्य उत्तर देखें, जो उस के विवरण में कूदने के लिए काफी स्वतंत्र लगते हैं।
कुछ साल पहले इस सवाल का जवाब देने के बाद, मेरा मानना है कि मैं उस प्रतिक्रिया को बेहतर बना सकता हूं और सरल बना सकता हूं ...
एक मोनाड एक फंक्शन कंपोजिशन तकनीक है, जो कंपोजिंग फंक्शन का उपयोग करके कुछ इनपुट परिदृश्यों के लिए ट्रीटमेंट को बाहरी बनाता है bind
, कंपोजिशन के दौरान प्री-प्रोसेस इनपुट को।
सामान्य रचना में, फ़ंक्शन, compose (>>)
अपने पूर्ववर्ती अनुक्रम में परिणाम के लिए रचित फ़ंक्शन को लागू करने के लिए उपयोग किया जाता है। महत्वपूर्ण रूप से, फंक्शन की रचना इसके इनपुट के सभी परिदृश्यों को संभालने के लिए आवश्यक है।
(x -> y) >> (y -> z)
इनपुट के पुनर्गठन से इस डिज़ाइन को बेहतर बनाया जा सकता है ताकि संबंधित राज्य अधिक आसानी से पूछताछ कर सकें। इसलिए, इसके बजाय केवल y
मूल्य Mb
ऐसे बन सकते हैं , उदाहरण के लिए, (is_OK, b)
यदि y
वैधता की धारणा शामिल है।
उदाहरण के लिए, जब इनपुट केवल संभवतः एक संख्या होती है, तो एक स्ट्रिंग को वापस करने के बजाय जिसमें ड्यूटिटली रूप से एक संख्या हो सकती है या नहीं, आप टाइप कर सकते bool
हैं एक वैध संख्या की उपस्थिति का संकेत और एक संख्या जैसे टुपल में bool * float
। रचित कार्यों को अब यह निर्धारित करने के लिए इनपुट स्ट्रिंग को पार्स करने की आवश्यकता नहीं होगी कि क्या कोई संख्या मौजूद है, लेकिन केवल bool
एक ट्यूपल के हिस्से का निरीक्षण कर सकता है ।
(Ma -> Mb) >> (Mb -> Mc)
यहां, फिर से, रचना स्वाभाविक रूप से होती है compose
और इसलिए प्रत्येक फ़ंक्शन को अपने इनपुट के सभी परिदृश्यों को व्यक्तिगत रूप से संभालना चाहिए, हालांकि ऐसा करना अब बहुत आसान है।
हालाँकि, क्या होगा यदि हम उन समयों के लिए पूछताछ के प्रयास को समाप्त कर सकते हैं जहाँ एक परिदृश्य को संभालना दिनचर्या है। उदाहरण के लिए, यदि इनपुट के रूप में ठीक नहीं है तो हमारा प्रोग्राम कुछ भी नहीं करता is_OK
है false
। यदि ऐसा किया गया, तो रचित कार्यों को स्वयं उस परिदृश्य को संभालने की आवश्यकता नहीं होगी, नाटकीय रूप से उनके कोड को सरल बनाने और पुन: उपयोग के दूसरे स्तर को प्रभावित करना होगा।
इस बाह्यकरण को प्राप्त करने के लिए हम एक फ़ंक्शन का उपयोग कर सकते हैं bind (>>=)
, composition
इसके बजाय प्रदर्शन करने के लिए compose
। जैसे, एक फ़ंक्शन के आउटपुट से मूल्यों को केवल दूसरे के इनपुट में स्थानांतरित करने के बजाय, Bind
के M
हिस्से का निरीक्षण करेंगे Ma
और यह तय करेंगे कि कंप्लीट किए गए फ़ंक्शन को कैसे और कैसे लागू किया जाए a
। बेशक, फ़ंक्शन bind
को विशेष M
रूप से हमारे विशेष रूप से परिभाषित किया जाएगा ताकि हम इसकी संरचना का निरीक्षण कर सकें और जो भी प्रकार का आवेदन करना चाहते हैं, कर सकें। बहरहाल, यह a
तब से कुछ भी हो सकता है , जब यह आवश्यक अनुप्रयोग को निर्धारित करने के लिए bind
केवल a
अछूता से रचित फ़ंक्शन में जाता है। इसके अतिरिक्त, स्वयं तैयार किए गए कार्यों को अब इससे निपटने की आवश्यकता नहीं हैM
इनपुट संरचना का हिस्सा या तो, उन्हें सरल बनाना। इसलिये...
(a -> Mb) >>= (b -> Mc)
या अधिक सफलतापूर्वक Mb >>= (b -> Mc)
संक्षेप में, एक मोनाड बाहरी हो जाता है और इस तरह कुछ इनपुट परिदृश्यों के उपचार के आसपास मानक व्यवहार प्रदान करता है, जब इनपुट उन्हें पर्याप्त रूप से उजागर करने के लिए डिज़ाइन किया जाता है। यह डिज़ाइन एक shell and content
मॉडल है जहाँ शेल में सम्मिलित फ़ंक्शन के अनुप्रयोग से संबंधित डेटा होता है और इसके द्वारा पूछताछ की जाती है और यह केवल bind
फ़ंक्शन के लिए उपलब्ध रहता है।
इसलिए, एक सनक तीन चीजें हैं:
M
मोनाड प्रासंगिक जानकारी रखने के लिए एक खोल, bind
समारोह सामग्री मूल्य के लिए बना कार्यों (रों) यह खोल के भीतर पाता है की अपने आवेदन में इस खोल जानकारी का उपयोग करने के लिए लागू है, और a -> Mb
निर्माण योग्य कार्य , ऐसे परिणाम तैयार करना जिनमें मोनैडिक प्रबंधन डेटा शामिल हैं।सामान्यतया, किसी फ़ंक्शन का इनपुट उसके आउटपुट की तुलना में कहीं अधिक प्रतिबंधात्मक होता है जिसमें त्रुटि स्थितियों जैसी चीजें शामिल हो सकती हैं; इसलिए, Mb
परिणाम संरचना आम तौर पर बहुत उपयोगी है। उदाहरण के लिए, डिवीजन ऑपरेटर एक नंबर नहीं देता है जब विभाजक होता है 0
।
इसके अतिरिक्त, monad
s में वे फ़ंक्शन शामिल हो सकते हैं , जो मान के a
प्रकार में Ma
, और सामान्य कार्यों में, मान a -> b
कार्यों में, a -> Mb
उनके परिणामों को अनुप्रयोग के बाद लपेटकर लपेटते हैं। बेशक, जैसे bind
, इस तरह के रैप फ़ंक्शन विशिष्ट हैं M
। एक उदाहरण:
let return a = [a]
let lift f a = return (f a)
bind
फ़ंक्शन का डिज़ाइन अपरिवर्तनीय डेटा संरचनाओं को मानता है और शुद्ध कार्य अन्य चीजें जटिल हो जाती हैं और गारंटी नहीं दी जा सकती है। जैसे कि, वहाँ विवादास्पद कानून हैं:
दिया हुआ...
M_
return = (a -> Ma)
f = (a -> Mb)
g = (b -> Mc)
फिर...
Left Identity : (return a) >>= f === f a
Right Identity : Ma >>= return === Ma
Associative : Ma >>= (f >>= g) === Ma >>= ((fun x -> f x) >>= g)
Associativity
इसका मतलब यह है कि bind
जब bind
भी लागू किया जाता है , तब तक मूल्यांकन के क्रम को बनाए रखता है। यही है, Associativity
ऊपर की परिभाषा में , कोष्ठक binding
के प्रारंभिक मूल्यांकन के बल f
और g
केवल एक फ़ंक्शन में परिणाम होगा जो Ma
पूरी होने की उम्मीद करता है bind
। इसलिए मूल्यांकन का Ma
निर्धारण उसके मूल्य पर लागू होने से पहले किया जाना चाहिए f
और इसके परिणामस्वरूप लागू किया जाना चाहिए g
।
एक मोनाड, प्रभावी रूप से, "टाइप ऑपरेटर" का एक रूप है। यह तीन काम करेगा। पहले यह "रैप" (या अन्यथा रूपांतरित करेगा) एक प्रकार के मूल्य को दूसरे प्रकार में (आमतौर पर "मोनडिक प्रकार" कहा जाता है)। दूसरी बात यह सभी ऑपरेशन (या फ़ंक्शंस) को मोनडिक प्रकार पर उपलब्ध अंतर्निहित प्रकार पर उपलब्ध कराएगी। अंत में यह एक समग्र मोनाड का उत्पादन करने के लिए अपने स्वयं को दूसरे मोनाड के साथ संयोजन के लिए समर्थन प्रदान करेगा।
विजुअल बेसिक / सी # में "शायद मोनड" मूल रूप से "अशक्त प्रकार" के बराबर है। यह एक गैर अशक्त प्रकार "T" लेता है और इसे "Nullable <T>" में परिवर्तित करता है, और फिर परिभाषित करता है कि सभी Nullable <T> पर बाइनरी ऑपरेटरों का क्या अर्थ है।
साइड इफेक्ट्स simillarly प्रतिनिधित्व कर रहे हैं। एक संरचना बनाई जाती है जो किसी फ़ंक्शन के रिटर्न मान के साथ साइड इफेक्ट्स का विवरण रखती है। "उठा हुआ" ऑपरेशन तब साइड इफेक्ट के आसपास कॉपी करता है क्योंकि मान फ़ंक्शन के बीच पारित हो जाते हैं।
उन्हें कई कारणों से "टाइप ऑपरेटर" के आसान नाम के बजाय "भिक्षु" कहा जाता है:
(यह भी देखें कि एक सन्यासी क्या है? )
मोनाड्स के लिए एक अच्छी प्रेरणा सिगफ़े (डैन पिपोनी) की आप आविष्कारित मोनाड हो सकते हैं! (और शायद आप पहले से ही हैं) । अन्य मोनाद ट्यूटोरियल के बहुत सारे हैं , जिनमें से कई गुमराह करने के लिए विभिन्न उपमाओं का उपयोग करते हुए "सरल शब्दों" में भिक्षुओं को समझाने की कोशिश की जाती है: यह मोनाद ट्यूटोरियल दोष है ; उनसे बचो।
जैसा कि DR MacIver हमें बताता है कि आपकी भाषा क्यों बेकार है :
इसलिए, हास्केल के बारे में मुझे जिन चीजों से नफरत है:
आइए स्पष्ट के साथ शुरू करते हैं। मोनाड ट्यूटोरियल। नहीं, सन्यासी नहीं। विशेष रूप से ट्यूटोरियल। वे अंतहीन हैं, अतिभारित हैं और प्रिय भगवान वे थकाऊ हैं। इसके अलावा, मैंने कभी कोई ठोस सबूत नहीं देखा कि वे वास्तव में मदद करें। कक्षा की परिभाषा पढ़ें, कुछ कोड लिखें, डरावने नाम पर जाएं।
आप कहते हैं कि आप शायद सन्यासी समझते हैं? अच्छा है, आप अपने रास्ते पर हैं। बस अन्य साधुओं का उपयोग करना शुरू करें और जल्द ही या बाद में आप समझ जाएंगे कि सामान्य रूप से कौन से भिक्षु हैं।
[यदि आप गणितीय रूप से उन्मुख हैं, तो आप दर्जनों ट्यूटोरियल्स को अनदेखा कर सकते हैं और परिभाषा सीख सकते हैं, या श्रेणी सिद्धांत में व्याख्यान का अनुसरण कर सकते हैं :) परिभाषा का मुख्य भाग यह है कि मोनाड एम में "टाइप कंस्ट्रक्टर" शामिल है जो प्रत्येक के लिए परिभाषित करता है। मौजूदा प्रकार "टी" एक नया प्रकार "एमटी", और "नियमित" प्रकार और "एम" प्रकारों के बीच आगे और पीछे जाने के कुछ तरीके।]
इसके अलावा, आश्चर्यजनक रूप से पर्याप्त, मोनाड्स के लिए सबसे अच्छा परिचय में से एक वास्तव में शुरुआती शैक्षणिक पत्रों में से एक है जिसमें कार्यात्मक प्रोग्रामिंग के लिए मोनाड्स , फिलिप वाडलर के मोनाड्स शामिल हैं । इसमें वास्तव में व्यावहारिक, गैर-तुच्छ प्रेरक उदाहरण हैं, कई कृत्रिम ट्यूटोरियल के विपरीत।
मोनड्स प्रवाह को नियंत्रित करने के लिए हैं कि डेटा के सार तत्व क्या हैं।
दूसरे शब्दों में, कई डेवलपर्स सेट्स, लिस्ट्स, डॉक्स (या हैशिंग, या मैप्स) और पेड़ों के विचार से सहज हैं। उन डेटा प्रकारों के भीतर कई विशेष मामले हैं (उदाहरण के लिए InsertionOrderPreservesIdentityHashMap)।
हालाँकि, जब प्रोग्राम "प्रवाह" से सामना होता है, तो कई डेवलपर्स कई अन्य निर्माणों से उजागर नहीं होते हैं यदि, स्विच / केस, डू, जबकि, गोटो (जीआरआर), और (शायद) क्लोजर।
तो, एक मोनाड बस एक नियंत्रण प्रवाह निर्माण है। मोनाड को बदलने के लिए एक बेहतर वाक्यांश 'नियंत्रण प्रकार' होगा।
जैसे, एक मॉनड के पास नियंत्रण तर्क, या कथन या फ़ंक्शन के लिए स्लॉट हैं - डेटा संरचनाओं में समकक्ष यह कहना होगा कि कुछ डेटा संरचनाएं आपको डेटा जोड़ने, और इसे निकालने की अनुमति देती हैं।
उदाहरण के लिए, "अगर" भिक्षु:
if( clause ) then block
इसके सरलतम में दो स्लॉट हैं - एक खंड, और एक ब्लॉक। if
इकाई आमतौर पर खंड के परिणाम का मूल्यांकन करने के लिए बनाया गया है, और अगर गलत नहीं, ब्लॉक का मूल्यांकन। कई डेवलपर्स मोनड्स से परिचित नहीं होते हैं, जब वे 'अगर' सीखते हैं, और प्रभावी तर्क लिखने के लिए केवल मोनड्स को समझना आवश्यक नहीं है।
मोनाड्स अधिक जटिल हो सकते हैं, उसी तरह से कि डेटा संरचनाएं अधिक जटिल हो सकती हैं, लेकिन मोनाड की कई व्यापक श्रेणियां हैं जिनके समान शब्दार्थ हो सकते हैं, लेकिन कार्यान्वयन और वाक्यविन्यास अलग-अलग हो सकते हैं।
बेशक, उसी तरह से जिस पर डेटा संरचनाओं को पुनरावृत्त किया जा सकता है, या ट्रैवर्स किए गए, मोनडेस का मूल्यांकन किया जा सकता है।
कंपाइलर में उपयोगकर्ता-परिभाषित मोनड के लिए समर्थन हो सकता है या नहीं भी हो सकता है। हास्केल निश्चित रूप से करता है। Ioke में कुछ समान क्षमताएं हैं, हालांकि भाषा में मोनाड शब्द का उपयोग नहीं किया गया है।
मेरा पसंदीदा मोनाद ट्यूटोरियल:
http://www.haskell.org/haskellwiki/All_About_Monads
("मोनड ट्यूटोरियल" के लिए Google खोज पर 170,000 हिट्स में से!)
@Stu: मोनाड्स का बिंदु आपको अन्यथा शुद्ध कोड में (आमतौर पर) अनुक्रमिक शब्दार्थ जोड़ने की अनुमति देना है; आप यहां तक कि मोनाड्स (मोनाड ट्रांसफॉर्मर्स का उपयोग करके) की रचना कर सकते हैं और उदाहरण के लिए त्रुटि से निपटने, साझा स्थिति और लॉगिंग के साथ पार्सिंग जैसे अधिक रोचक और जटिल संयुक्त शब्दार्थ प्राप्त कर सकते हैं। यह सब शुद्ध कोड में संभव है, मोनाड्स आपको इसे दूर करने और मॉड्यूलर पुस्तकालयों (हमेशा प्रोग्रामिंग में अच्छा) में इसका पुन: उपयोग करने की अनुमति देता है, साथ ही इसे अनिवार्य रूप से देखने के लिए सुविधाजनक सिंटैक्स प्रदान करता है।
हास्केल के पास पहले से ही ऑपरेटर ओवरलोडिंग है [1]: यह प्रकार की कक्षाओं का उपयोग करता है जिस तरह से जावा या सी # में इंटरफेस का उपयोग किया जा सकता है, लेकिन हास्केल बस ऐसा होता है कि गैर-अल्फ़ान्यूमेरिक टोकन जैसे + && और> को अनन्त पहचानकर्ता की अनुमति देता है। यदि आप "अर्धविराम को ओवरलोड कर रहे हैं तो इसका मतलब है कि इसे देखने के अपने तरीके से ओवरलोडिंग करना केवल ऑपरेटर है [2]। यह काले जादू की तरह लगता है और "सेमीकॉलन को अधिभारित करने के लिए परेशानी" के लिए पूछ रहा है (चित्र इस विचार का हवा हो रही पर्ल हैकर), लेकिन मुद्दा यह है कि बिना मोनाडेस के कोई अर्धविराम नहीं है, क्योंकि विशुद्ध रूप से कार्यात्मक कोड को स्पष्ट अनुक्रमण की आवश्यकता नहीं है या अनुमति नहीं है।
यह सभी की तुलना में अधिक जटिल लगता है। सिगफेक का लेख बहुत अच्छा है, लेकिन इसे समझाने के लिए हास्केल का उपयोग किया जाता है, जो हास्केल को समझने के लिए मोनाड्स को समझने और मोनाड्स को हास्केल को समझने के लिए चिकन और अंडे की समस्या को तोड़ने में विफल रहता है।
[१] यह भिक्षुओं का एक अलग मुद्दा है लेकिन भिक्षु हास्केल के ऑपरेटर ओवरलोडिंग फीचर का उपयोग करते हैं।
[२] यह भी एक निरीक्षण है क्योंकि मोनडिक क्रियाओं के लिए परिचालक >> = (उच्चारण "बाइंड") है, लेकिन इसमें सिन्सेटिक शुगर ("डू") है जो आपको ब्रेसिज़ और सेमीकोलन और / इंडेंटेशन और न्यूलाइन्स का उपयोग करने देता है।
मैं एक अलग तरीके से हाल ही में मोनाड्स के बारे में सोच रहा हूं। मैं उन्हें गणितीय तरीके से निष्पादन आदेश को अमूर्त करने के बारे में सोच रहा हूं , जो नए प्रकार के बहुरूपता को संभव बनाता है।
यदि आप एक अनिवार्य भाषा का उपयोग कर रहे हैं, और आप क्रम में कुछ अभिव्यक्ति लिखते हैं, तो कोड हमेशा उसी क्रम में चलता है।
और सरल मामले में, जब आप एक सनक का उपयोग करते हैं, तो यह वही महसूस करता है - आप अभिव्यक्तियों की एक सूची को परिभाषित करते हैं जो क्रम में होती हैं। सिवाय इसके कि, आप किस मोनाड का उपयोग करते हैं, आपके कोड क्रम में चल सकते हैं (जैसे कि IO मोनैड), एक ही बार में कई मदों के समानांतर (जैसे सूची मोनाद) में, यह (शायद सन्यासी में) , यह बाद में फिर से शुरू होने की तरह (फिर से शुरू होने वाले मोनाड की तरह) के माध्यम से भाग को विराम दे सकता है, यह शुरुआत से शुरू हो सकता है (जैसे एक लेनदेन मोनाड), या यह अन्य विकल्पों की कोशिश करने के लिए पार्टवे को फिर से कर सकता है (जैसे एक तर्क मोनाड में) ।
और क्योंकि मोनाड्स बहुरूपिक हैं, इसलिए आपकी आवश्यकताओं के आधार पर, विभिन्न मोनाडों में समान कोड चलाना संभव है।
साथ ही, कुछ मामलों में, एक ही समय में कई विशेषताओं को प्राप्त करने के लिए (एक साथ ट्रांसफॉर्मर के साथ) मोनड को जोड़ना संभव है।
मैं अभी भी भिक्षुओं के लिए नया हूं, लेकिन मुझे लगा कि मैं एक लिंक साझा करूंगा, जिसमें मैंने पाया कि पढ़ने में बहुत अच्छा लगा (PICTURES के साथ !!): http://www.matusiak.eu/numerodix/blog/2012/3/11/ !! भिक्षुओं के लिए-आम आदमी / (कोई संबद्धता नहीं)
मूल रूप से, लेख से मुझे जो गर्मजोशी और फजी अवधारणा मिली, वह अवधारणा थी कि भिक्षु मूल रूप से एडाप्टर्स हैं, जो असंगत कार्यों को एक कंपोज़ेबल फैशन में काम करने की अनुमति देते हैं, अर्थात कई कार्यों को मिलाने और असंगत वापसी की चिंता किए बिना मिश्रण और मिलान करने में सक्षम होते हैं। प्रकार और ऐसे। जब हम इन एडेप्टर को बनाने की कोशिश कर रहे हैं, तो BIND फ़ंक्शन सेब और संतरे के साथ सेब रखने के आरोप में है। और LIFT फ़ंक्शन "निचले स्तर" फ़ंक्शन लेने और उन्हें BIND फ़ंक्शंस के साथ काम करने के लिए "अपग्रेड" करने के साथ-साथ कंपोज़ेबल भी है।
मुझे आशा है कि मुझे यह सही मिला, और इससे भी महत्वपूर्ण बात, आशा है कि लेख में भिक्षुओं पर एक मान्य दृष्टिकोण है। यदि और कुछ नहीं, तो इस लेख ने भिक्षुओं के बारे में अधिक जानने के लिए मेरी भूख को बढ़ाने में मदद की।
ऊपर दिए गए उत्कृष्ट उत्तरों के अलावा, मैं आपको निम्नलिखित लेख (पैट्रिक थॉमसन द्वारा) के लिए एक लिंक प्रदान करता हूं, जो जावास्क्रिप्ट लाइब्रेरी jQuery के लिए अवधारणा से संबंधित साधुओं को समझाता है (और डोम में हेरफेर करने के लिए "विधि का उपयोग करने का तरीका") : jQuery एक मोनाड है
JQuery प्रलेखन खुद "बिल्डर पैटर्न" जो शायद अधिक परिचित है के बारे में शब्द "इकाई" लेकिन वार्ता का उल्लेख नहीं करता। यह इस तथ्य को नहीं बदलता है कि आपके पास एक उचित मोनाड है शायद इसे साकार किए बिना भी।
मोनाड्स मेटाफ़ोर्स नहीं हैं , लेकिन एक सामान्य पैटर्न से उभरने वाला व्यावहारिक रूप से उपयोगी अमूर्त है, जैसा कि डैनियल स्पिवक बताते हैं।
एक मोनाड एक साथ संगणना के संयोजन का एक तरीका है जो एक सामान्य संदर्भ साझा करता है। यह पाइप के नेटवर्क के निर्माण जैसा है। नेटवर्क का निर्माण करते समय, इसके माध्यम से बहने वाला कोई डेटा नहीं है। लेकिन जब मैंने I बाइंड ’और then रिटर्न’ के साथ सभी बिट्स को एक साथ जोड़कर समाप्त कर दिया है, तो मैं कुछ ऐसा करता हूं runMyMonad monad data
और डेटा पाइप से बहता है।
व्यवहार में, मोनाड फंक्शन कंपोजिशन ऑपरेटर का एक कस्टम कार्यान्वयन है जो साइड इफेक्ट्स और असंगत इनपुट और रिटर्न वैल्यूज़ (चैनिंग के लिए) का ध्यान रखता है।
अगर मैंने सही तरीके से समझा है, तो IEnumerable मोनड्स से लिया गया है। मुझे आश्चर्य है कि अगर हम सी # दुनिया से उन लोगों के लिए दृष्टिकोण का एक दिलचस्प कोण हो सकता है?
इसके लायक क्या है, यहां ट्यूटोरियल के कुछ लिंक दिए गए हैं, जिन्होंने मेरी मदद की (और नहीं, मुझे अभी भी समझ नहीं आया है कि भिक्षु क्या हैं)।
वहाँ सीखने के दौरान जिन दो चीज़ों ने मेरी सबसे अच्छी मदद की:
अध्याय 8, "कार्यात्मक पार्सर्स," ग्राहम हटन की पुस्तक हास्केल में प्रोग्रामिंग से । यह वास्तव में मोनड्स का उल्लेख नहीं करता है, लेकिन यदि आप अध्याय के माध्यम से काम कर सकते हैं और वास्तव में इसमें सब कुछ समझ सकते हैं, विशेष रूप से कैसे बाँध संचालन के एक अनुक्रम का मूल्यांकन किया जाता है, तो आप भिक्षुओं के आंतरिक को समझेंगे। यह कई कोशिशें करने की अपेक्षा करें।
ट्यूटोरियल सभी मोनाड्स के बारे में । यह उनके उपयोग के कई अच्छे उदाहरण देता है, और मुझे यह कहना है कि एपेंडेक्स में सादृश्य मैंने मेरे लिए काम किया।
मोनॉइड कुछ ऐसा प्रतीत होता है जो यह सुनिश्चित करता है कि एक मोनॉइड और एक समर्थित प्रकार पर परिभाषित सभी ऑपरेशन हमेशा मोनॉइड के अंदर एक समर्थित प्रकार लौटाएंगे। जैसे, कोई संख्या + कोई भी संख्या = कोई संख्या, कोई त्रुटि नहीं।
जबकि विभाजन दो अंशों को स्वीकार करता है, और एक भिन्नात्मक रिटर्न देता है, जो कि हेकेल में इनफिनिटी के रूप में शून्य द्वारा परिभाषित विभाजन होता है, जो कि (जो आंशिक रूप से भिन्न होता है) ...
किसी भी स्थिति में, यह प्रतीत होता है कि मोनाड्स यह सुनिश्चित करने का एक तरीका है कि आपके संचालन की श्रृंखला एक पूर्वानुमेय तरीके से व्यवहार करती है, और एक फ़ंक्शन जो कि Num -> Num होने का दावा करता है, जो Num-> Num के किसी अन्य फ़ंक्शन के साथ बना है, जिसे x के साथ बुलाया गया है। कहते हैं, मिसाइलों आग।
दूसरी ओर, अगर हमारे पास एक फ़ंक्शन है जो मिसाइलों को फायर करता है, तो हम इसे अन्य कार्यों के साथ बना सकते हैं जो मिसाइलों को भी आग लगाते हैं, क्योंकि हमारा इरादा स्पष्ट है - हम मिसाइलों को फायर करना चाहते हैं - लेकिन यह कोशिश नहीं करेगा कुछ अजीब कारण के लिए "हैलो वर्ल्ड" प्रिंट करना।
हास्केल में, मुख्य प्रकार IO (), या IO [()] का है, आसक्ति अजीब है और मैं इस पर चर्चा नहीं करूंगा लेकिन यहां मैं जो सोचता हूं वह होता है:
यदि मेरे पास मुख्य है, तो मैं चाहता हूं कि यह कार्रवाई की एक श्रृंखला करे, जिस कारण से मैं कार्यक्रम चलाता हूं वह एक प्रभाव पैदा करता है - आमतौर पर हालांकि आईओ। इस प्रकार मैं मुख्य रूप से एक साथ IO परिचालनों की श्रृंखला बना सकता हूं - IO करें, और कुछ नहीं।
अगर मैं कुछ ऐसा करने की कोशिश करता हूं जो "आईओ वापस नहीं करता है", तो कार्यक्रम शिकायत करेगा कि श्रृंखला प्रवाह नहीं करती है, या मूल रूप से "यह कैसे संबंधित है जो हम करने की कोशिश कर रहे हैं - एक आईओ कार्रवाई", यह बल के लिए प्रकट होता है प्रोग्रामर ने बिना सोचे-समझे, मिसाइलों को फायर करने के बारे में, बिना छंटाई के एल्गोरिदम बनाते हुए, अपनी ट्रेन को रखने के लिए - जो प्रवाह नहीं करता है।
मूल रूप से, मोनाडर्स कंपाइलर को एक टिप देते हैं कि "अरे, आप इस फ़ंक्शन को जानते हैं जो यहां एक नंबर देता है, यह वास्तव में हमेशा काम नहीं करता है, यह कभी-कभी एक नंबर का उत्पादन कर सकता है, और कभी-कभी कुछ भी नहीं, बस इसे अंदर रखें। मन"। यह जानकर, यदि आप एक राक्षसी कार्रवाई को जोर देने की कोशिश करते हैं, तो मोनडिक कार्रवाई एक संकलित समय अपवाद के रूप में कार्य कर सकती है "अरे, यह वास्तव में एक संख्या नहीं है, यह एक संख्या हो सकती है, लेकिन आप यह नहीं मान सकते हैं, कुछ करें यह सुनिश्चित करने के लिए कि प्रवाह स्वीकार्य है। ” जो अप्रत्याशित कार्यक्रम व्यवहार को काफी हद तक रोकता है।
ऐसा प्रतीत होता है कि भिक्षु शुद्धता के बारे में नहीं हैं, न ही नियंत्रण के बारे में, बल्कि एक ऐसी श्रेणी की पहचान बनाए रखने के बारे में, जिस पर सभी व्यवहार अनुमानित और परिभाषित हैं, या संकलन नहीं करते हैं। जब आप कुछ करने की उम्मीद कर रहे हैं, तो आप कुछ नहीं कर सकते हैं, और यदि आप कुछ भी नहीं करने की उम्मीद कर रहे हैं तो आप कुछ नहीं कर सकते (दृश्यमान)।
सबसे बड़ा कारण जो मैं मोनाड्स के बारे में सोच सकता था, वह है - प्रक्रियात्मक / ओओपी कोड को देखें, और आप देखेंगे कि आपको नहीं पता है कि कार्यक्रम कहां शुरू होता है, न ही समाप्त होता है, आप सभी देखते हैं कि बहुत अधिक कूद और बहुत सारा गणित है , जादू, और मिसाइल। आप इसे बनाए रखने में सक्षम नहीं होंगे, और यदि आप कर सकते हैं, तो आप पूरे कार्यक्रम के आसपास अपने दिमाग को लपेटने में काफी समय बिताएंगे, इससे पहले कि आप इसके किसी भी हिस्से को समझ सकें, क्योंकि इस संदर्भ में मॉड्यूलरिटी अन्योन्याश्रित "वर्गों" पर आधारित है कोड, जहां कोड को दक्षता / अंतर-संबंध के वादे के लिए यथासंभव संबंधित होने के लिए अनुकूलित किया गया है। मोनाड्स बहुत ठोस हैं, और अच्छी तरह से परिभाषा द्वारा परिभाषित किए गए हैं, और यह सुनिश्चित करते हैं कि कार्यक्रम का प्रवाह विश्लेषण करना संभव है, और उन हिस्सों को अलग करना जो विश्लेषण करना मुश्किल है - जैसा कि वे स्वयं मोनाड हैं। एक भिक्षु प्रतीत होता है " या ब्रह्मांड को नष्ट या यहां तक कि विकृत समय - हमें कोई पता नहीं है और न ही कोई गारंटी है कि यह क्या है क्या यह है। एक भिक्षु यह कहता है कि यह क्या है? जो बहुत शक्तिशाली है। या ब्रह्मांड को नष्ट या यहां तक कि विकृत समय - हमें कोई पता नहीं है और न ही कोई गारंटी है कि यह क्या है क्या यह है। एक भिक्षु यह कहता है कि यह क्या है? जो बहुत शक्तिशाली है।
"वास्तविक दुनिया" में सभी चीजें भिक्षुओं के रूप में दिखाई देती हैं, इस अर्थ में कि यह भ्रम को रोकने वाले निश्चित अवलोकन कानूनों द्वारा बाध्य है। इसका मतलब यह नहीं है कि हमें कक्षाओं को बनाने के लिए इस ऑब्जेक्ट के सभी कार्यों की नकल करना है, इसके बजाय हम बस "एक वर्ग एक वर्ग" कह सकते हैं, कुछ भी नहीं लेकिन एक वर्ग, यहां तक कि एक आयत भी नहीं और न ही एक सर्कल, और "एक वर्ग का क्षेत्र है" इसकी मौजूदा आयामों में से एक की लंबाई अपने आप से कई गुना अधिक है। चाहे कोई भी वर्ग हो, अगर यह 2 डी अंतरिक्ष में एक वर्ग है, तो यह क्षेत्र बिल्कुल कुछ भी नहीं हो सकता है, लेकिन इसकी लंबाई चुकता है, यह साबित करने के लिए लगभग तुच्छ है। यह बहुत शक्तिशाली है क्योंकि हमें यह सुनिश्चित करने के लिए दावे करने की आवश्यकता नहीं है कि हमारी दुनिया जिस तरह से है, हम अपने कार्यक्रमों को रोकने के लिए वास्तविकता के निहितार्थ का उपयोग करते हैं।
Im बहुत ज्यादा गलत होने की गारंटी है, लेकिन मुझे लगता है कि इससे किसी को मदद मिल सकती है, इसलिए उम्मीद है कि यह किसी की मदद करेगा।
स्काला के संदर्भ में आपको सबसे सरल परिभाषा मिलेगी। मूल रूप से फ्लैटपाइप (या बाइंड) 'साहचर्य' है और एक पहचान मौजूद है।
trait M[+A] {
def flatMap[B](f: A => M[B]): M[B] // AKA bind
// Pseudo Meta Code
def isValidMonad: Boolean = {
// for every parameter the following holds
def isAssociativeOn[X, Y, Z](x: M[X], f: X => M[Y], g: Y => M[Z]): Boolean =
x.flatMap(f).flatMap(g) == x.flatMap(f(_).flatMap(g))
// for every parameter X and x, there exists an id
// such that the following holds
def isAnIdentity[X](x: M[X], id: X => M[X]): Boolean =
x.flatMap(id) == x
}
}
उदाहरण के लिए
// These could be any functions
val f: Int => Option[String] = number => if (number == 7) Some("hello") else None
val g: String => Option[Double] = string => Some(3.14)
// Observe these are identical. Since Option is a Monad
// they will always be identical no matter what the functions are
scala> Some(7).flatMap(f).flatMap(g)
res211: Option[Double] = Some(3.14)
scala> Some(7).flatMap(f(_).flatMap(g))
res212: Option[Double] = Some(3.14)
// As Option is a Monad, there exists an identity:
val id: Int => Option[Int] = x => Some(x)
// Observe these are identical
scala> Some(7).flatMap(id)
res213: Option[Int] = Some(7)
scala> Some(7)
res214: Some[Int] = Some(7)
नोट कड़ाई से एक की परिभाषा बोल कार्यात्मक प्रोग्रामिंग में इकाई एक की परिभाषा के समान नहीं है श्रेणी थ्योरी में इकाई , जिनमें से बदल जाता है में परिभाषित किया गया है map
और flatten
। हालांकि वे कुछ मैपिंग के तहत समान हैं। यह प्रस्तुतियाँ बहुत अच्छी हैं: http://www.slideshare.net/samthemonad/monad-pretation-scala-as-a-category
यह उत्तर एक प्रेरक उदाहरण के साथ शुरू होता है, उदाहरण के माध्यम से काम करता है, एक साधु का उदाहरण प्राप्त करता है, और औपचारिक रूप से "मठ" को परिभाषित करता है।
Pseudocode में इन तीन कार्यों पर विचार करें:
f(<x, messages>) := <x, messages "called f. ">
g(<x, messages>) := <x, messages "called g. ">
wrap(x) := <x, "">
f
फॉर्म की एक ऑर्डर की गई जोड़ी लेता है <x, messages>
और एक ऑर्डर की गई जोड़ी लौटाता है। यह पहले आइटम को अछूता छोड़ देता है और "called f. "
दूसरे आइटम को जोड़ता है। उसी के साथ g
।
आप इन कार्यों की रचना कर सकते हैं और अपना मूल मूल्य प्राप्त कर सकते हैं, साथ ही एक स्ट्रिंग जो यह दिखाती है कि कार्यों को किस क्रम में बुलाया गया है:
f(g(wrap(x)))
= f(g(<x, "">))
= f(<x, "called g. ">)
= <x, "called g. called f. ">
आप इस तथ्य को नापसंद करते हैं f
और g
पिछले लॉगिंग जानकारी के लिए अपने स्वयं के लॉग संदेशों को जोड़ने के लिए जिम्मेदार हैं। (केवल तर्क के लिए कल्पना करें कि तार जोड़ने के बजाय, f
औरg
जोड़ी के दूसरे आइटम पर जटिल तर्क करना चाहिए। यह जटिल तर्क को दो - या अधिक - विभिन्न कार्यों में दोहराने के लिए एक दर्द होगा।)
आप सरल कार्य लिखना पसंद करते हैं:
f(x) := <x, "called f. ">
g(x) := <x, "called g. ">
wrap(x) := <x, "">
लेकिन जब आप उन्हें लिखें तो क्या होगा:
f(g(wrap(x)))
= f(g(<x, "">))
= f(<<x, "">, "called g. ">)
= <<<x, "">, "called g. ">, "called f. ">
समस्या यह है कि एक जोड़ी को एक समारोह में पारित करना आपको वह नहीं देता है जो आप चाहते हैं। लेकिन क्या होगा अगर आप किसी फंक्शन में एक जोड़ी खिला सकते हैं :
feed(f, feed(g, wrap(x)))
= feed(f, feed(g, <x, "">))
= feed(f, <x, "called g. ">)
= <x, "called g. called f. ">
feed(f, m)
"फ़ीड m
इन f
" के रूप में पढ़ें । करने के लिए फ़ीड एक जोड़ी <x, messages>
एक समारोह में f
करने के लिए है पारित x
में f
, मिल <y, message>
के बाहर f
, और वापसी <y, messages message>
।
feed(f, <x, messages>) := let <y, message> = f(x)
in <y, messages message>
ध्यान दें कि जब आप अपने कार्यों के साथ तीन चीजें करते हैं तो क्या होता है:
पहला: यदि आप एक मूल्य लपेटते हैं और फिर परिणामस्वरूप जोड़ी को एक फ़ंक्शन में खिलाते हैं :
feed(f, wrap(x))
= feed(f, <x, "">)
= let <y, message> = f(x)
in <y, "" message>
= let <y, message> = <x, "called f. ">
in <y, "" message>
= <x, "" "called f. ">
= <x, "called f. ">
= f(x)
यह फ़ंक्शन में मान को पारित करने के समान है।
दूसरा: यदि आप एक जोड़ी में खिलाते हैं wrap
:
feed(wrap, <x, messages>)
= let <y, message> = wrap(x)
in <y, messages message>
= let <y, message> = <x, "">
in <y, messages message>
= <x, messages "">
= <x, messages>
इससे जोड़ी नहीं बदलती।
तीसरा: यदि आप एक ऐसे फंक्शन को परिभाषित करते हैं जो लेता है x
और उसमें फीड करता g(x)
है f
:
h(x) := feed(f, g(x))
और इसमें एक जोड़ी खिलाएं:
feed(h, <x, messages>)
= let <y, message> = h(x)
in <y, messages message>
= let <y, message> = feed(f, g(x))
in <y, messages message>
= let <y, message> = feed(f, <x, "called g. ">)
in <y, messages message>
= let <y, message> = let <z, msg> = f(x)
in <z, "called g. " msg>
in <y, messages message>
= let <y, message> = let <z, msg> = <x, "called f. ">
in <z, "called g. " msg>
in <y, messages message>
= let <y, message> = <x, "called g. " "called f. ">
in <y, messages message>
= <x, messages "called g. " "called f. ">
= feed(f, <x, messages "called g. ">)
= feed(f, feed(g, <x, messages>))
यह जोड़ी में g
खिलाने और परिणामस्वरूप जोड़ी को खिलाने के समान है f
।
आपके पास अधिकांश एक सन्यासी है। अब आपको बस अपने प्रोग्राम के डेटा प्रकारों के बारे में जानना होगा।
किस प्रकार का मूल्य है <x, "called f. ">
? खैर, यह इस बात पर निर्भर करता है कि मूल्य किस प्रकार का x
है। यदि x
प्रकार है t
, तो आपकी जोड़ी " t
और स्ट्रिंग की जोड़ी" प्रकार का एक मूल्य है । उस प्रकार को बुलाओ M t
।
M
एक प्रकार का कंस्ट्रक्टर है: M
अकेला एक प्रकार को संदर्भित नहीं करता है, लेकिन M _
एक प्रकार को संदर्भित करता है जब आप एक प्रकार के साथ रिक्त को भरते हैं। एक M int
एक int और एक स्ट्रिंग की एक जोड़ी है। एक M string
एक स्ट्रिंग और एक स्ट्रिंग की एक जोड़ी है। आदि।
बधाई हो, आपने एक सन्यासी बनाया है!
औपचारिक रूप से, आपका मोद तुगलक है <M, feed, wrap>
।
एक मोदक एक टुपल है <M, feed, wrap>
जहां:
M
एक प्रकार का निर्माता है।feed
एक (समारोह है कि एक लेता है लेता है t
और एक रिटर्न M u
) और एक M t
और रिटर्न एक M u
।wrap
एक लेता है v
और एक रिटर्न देता है M v
।t
, u
और v
कोई भी तीन प्रकार हैं जो समान हो सकते हैं या नहीं भी हो सकते हैं। एक संन्यासी आपके विशिष्ट मठ के लिए आपके द्वारा सिद्ध किए गए तीन गुणों को संतुष्ट करता है:
दूध पिलाने की एक लिपटे t
एक समारोह में रूप में ही है गुजर unwrapped t
समारोह में।
औपचारिक रूप से: feed(f, wrap(x)) = f(x)
एक दूध पिलाने M t
में wrap
करने के लिए कुछ नहीं करता है M t
।
औपचारिक रूप से: feed(wrap, m) = m
एक फ़ंक्शन में एक M t
(यह कहते हैं m
) खिला कि
t
मेंg
M u
(कॉल n
) मिलता हैg
n
हैf
के समान है
m
मेंg
n
हैg
n
मेंf
औपचारिक रूप से: feed(h, m) = feed(f, feed(g, m))
कहांh(x) := feed(f, g(x))
आमतौर पर, feed
कहा जाता है bind
( >>=
हास्केल में AKA ) और wrap
कहा जाता है return
।
मैं Monad
हास्केल के संदर्भ में समझाने की कोशिश करूंगा ।
कार्यात्मक प्रोग्रामिंग में, फ़ंक्शन रचना महत्वपूर्ण है। यह हमारे कार्यक्रम को छोटे, आसानी से पढ़े जाने वाले कार्यों से मिलकर बना देता है।
मान लीजिए कि हमारे दो कार्य हैं: g :: Int -> String
और f :: String -> Bool
।
हम कर सकते हैं (f . g) x
, जो बस के रूप में ही है f (g x)
, जहां x
एक Int
मूल्य है।
रचना करते समय / एक फंक्शन का परिणाम दूसरे पर लागू करने के लिए, टाइप मैच होने महत्वपूर्ण है। उपरोक्त मामले में, परिणाम जिस प्रकार से लौटाया g
जाना चाहिए वह उसी प्रकार होना चाहिए जिस प्रकार से स्वीकार किया जाता हैf
।
लेकिन कभी-कभी मूल्य संदर्भों में होते हैं, और यह प्रकारों को लाइन करने के लिए थोड़ा कम आसान बनाता है। (संदर्भों में मान होना बहुत उपयोगी है। उदाहरण के लिए, Maybe Int
प्रकार एक Int
मान का प्रतिनिधित्व करता है जो वहां नहीं हो सकता है, यह IO String
प्रकार एक String
मान का प्रतिनिधित्व करता है जो कुछ दुष्प्रभावों के प्रदर्शन के परिणामस्वरूप होता है।)
मान लीजिए कि अब हम करते हैं g1 :: Int -> Maybe String
और f1 :: String -> Maybe Bool
। g1
और f1
बहुत समान हैं g
औरf
क्रमशः।
हम (f1 . g1) x
या नहीं कर सकते f1 (g1 x)
, जहां x
एक Int
मूल्य है। परिणाम जिस प्रकार से लौटा है g1
वह f1
उम्मीद नहीं है।
हम रचना कर सकते हैं f
और ऑपरेटर के g
साथ .
, लेकिन अब हम f1
और g1
साथ नहीं बना सकते .
। समस्या यह है कि हम किसी फ़ंक्शन के संदर्भ में एक मान को सीधे मान नहीं सकते हैं जो किसी संदर्भ में एक मूल्य की अपेक्षा करता है।
क्या यह अच्छा नहीं होगा यदि हम एक ऑपरेटर को रचना के लिए पेश करें g1
और f1
, जैसे कि हम लिख सकते हैं (f1 OPERATOR g1) x
? g1
एक संदर्भ में एक मूल्य देता है। मान को संदर्भ से बाहर ले जाया जाएगा और लागू किया जाएगा f1
। और हां, हमारे पास एक ऐसा ऑपरेटर है। यह है <=<
।
हमारे पास भी है >>=
ऑपरेटर हमारे लिए सटीक समान काम करता है, हालांकि थोड़ा अलग वाक्यविन्यास में।
हम लिखते हैं g1 x >>= f1
:। g1 x
एक Maybe Int
मूल्य है। >>=
ऑपरेटर कि लेने में मदद करता है Int
"शायद-नहीं-वहाँ" संदर्भ से बाहर मूल्य, और उसे लागू करने के f1
। का परिणाम f1
, जो एक है Maybe Bool
, पूरे >>=
ऑपरेशन का परिणाम होगा ।
और आखिरकार, क्यों Monad
उपयोगी है? क्योंकि Monad
एक प्रकार का वर्ग है जो >>=
ऑपरेटर को परिभाषित करता है , बहुत ही उस Eq
प्रकार का वर्ग है जो परिभाषित करता है ==
और/=
ऑपरेटर ऑपरेटरों ।
निष्कर्ष निकालने के लिए, Monad
प्रकार वर्ग को परिभाषित करता है>>=
ऑपरेटर है जो हमें एक संदर्भ में मानों को पारित करने की अनुमति देता है (हम इन मौद्रिक मूल्यों को उन कार्यों के लिए कहते हैं) जो संदर्भ में मूल्यों की अपेक्षा नहीं करते हैं। संदर्भ का ध्यान रखा जाएगा।
अगर यहां एक बात याद रखने की है, तो वह यह है कि Monad
फ़ंक्शन संरचना को अनुमति दें जिसमें संदर्भों में मान शामिल हैं ।
{-# LANGUAGE InstanceSigs #-}
newtype Id t = Id t
instance Monad Id where
return :: t -> Id t
return = Id
(=<<) :: (a -> Id b) -> Id a -> Id b
f =<< (Id x) = f x
$
कार्यों के अनुप्रयोग ऑपरेटर
forall a b. a -> b
विहित रूप से परिभाषित किया गया है
($) :: (a -> b) -> a -> b
f $ x = f x
infixr 0 $
हास्केल-आदिम फ़ंक्शन एप्लिकेशन f x
( infixl 10
) के संदर्भ में ।
संरचना .
के संदर्भ में परिभाषित किया गया है $
के रूप में
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \ x -> f $ g x
infixr 9 .
और समकक्षों को संतुष्ट करता है forall f g h.
f . id = f :: c -> d Right identity
id . g = g :: b -> c Left identity
(f . g) . h = f . (g . h) :: a -> d Associativity
.
साहचर्य है, और id
इसकी दाईं और बाईं पहचान है।
प्रोग्रामिंग में, एक मोनाड एक फनकार टाइप कंस्ट्रक्टर होता है जिसमें मोनैड टाइप क्लास होता है। परिभाषा और कार्यान्वयन के कई समतुल्य रूप हैं, प्रत्येक में मोनड अमूर्तता के बारे में थोड़ा अलग अंतर्ज्ञान है।
एक functor एक प्रकार निर्माता है f
एक तरह से * -> *
functor प्रकार वर्ग का एक उदाहरण के साथ।
{-# LANGUAGE KindSignatures #-}
class Functor (f :: * -> *) where
map :: (a -> b) -> (f a -> f b)
वैधानिक रूप से लागू प्रकार प्रोटोकॉल का पालन करने के अलावा, फफूंद प्रकार वर्ग के उदाहरणों को बीजीय फफूंद कानूनों का पालन करना चाहिए forall f g.
map id = id :: f t -> f t Identity
map f . map g = map (f . g) :: f a -> f c Composition / short cut fusion
फन्नेटर कंप्यूटर्स के प्रकार होते हैं
forall f t. Functor f => f t
एक संगणना c r
में संदर्भ के भीतर परिणाम शामिल होते हैं ।r
c
असमान मोनोडिक फ़ंक्शंस या क्लेज़ली तीरों का प्रकार होता है
forall m a b. Functor m => a -> m b
क्लेसी तीर ऐसे कार्य हैं जो एक तर्क लेते हैं a
और एक मोनडिक अभिकलन वापस करते हैंm b
।
क्लेडिस ट्रिपल के संदर्भ में मोनोड को कैनोनिक रूप से परिभाषित किया गया है forall m. Functor m =>
(m, return, (=<<))
प्रकार वर्ग के रूप में लागू किया गया
class Functor m => Monad m where
return :: t -> m t
(=<<) :: (a -> m b) -> m a -> m b
infixr 1 =<<
Kleisli पहचान return
एक Kleisli तीर एक मूल्य को बढ़ावा देता है t
monadic संदर्भ में m
। एक्सटेंशन या क्लेसीली आवेदन एक संगणना के परिणामों के लिए =<<
क्लेस्ली तीर लागू a -> m b
करता हैm a
।
क्लेसली रचना <=<
को विस्तार के संदर्भ में परिभाषित किया गया है
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = \ x -> f =<< g x
infixr 1 <=<
<=<
दो क्लीसली तीरों की रचना करता है, दाएं तीर के अनुप्रयोग के परिणामों के लिए बाएं तीर को लागू करता है।
मोनाद प्रकार के वर्ग के उदाहरणों को मोनाद कानूनों का पालन करना चाहिए , जो कि ज्यादातर क्लेसीली रचना के संदर्भ में कहा गया है:forall f g h.
f <=< return = f :: c -> m d Right identity
return <=< g = g :: b -> m c Left identity
(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d Associativity
<=<
साहचर्य है, और return
इसकी दाईं और बाईं पहचान है।
पहचान का प्रकार
type Id t = t
प्रकारों पर पहचान कार्य है
Id :: * -> *
एक फ़नकार के रूप में व्याख्या की गई,
return :: t -> Id t
= id :: t -> t
(=<<) :: (a -> Id b) -> Id a -> Id b
= ($) :: (a -> b) -> a -> b
(<=<) :: (b -> Id c) -> (a -> Id b) -> (a -> Id c)
= (.) :: (b -> c) -> (a -> b) -> (a -> c)
विहित हास्केल में, पहचान मोनड को परिभाषित किया गया है
newtype Id t = Id t
instance Functor Id where
map :: (a -> b) -> Id a -> Id b
map f (Id x) = Id (f x)
instance Monad Id where
return :: t -> Id t
return = Id
(=<<) :: (a -> Id b) -> Id a -> Id b
f =<< (Id x) = f x
एक विकल्प प्रकार
data Maybe t = Nothing | Just t
सांकेतिक शब्दों में बदलना गणना है Maybe t
कि जरूरी नहीं कि एक परिणाम देता है t
, अभिकलन "विफल" हो सकता है। विकल्प मोनड को परिभाषित किया गया है
instance Functor Maybe where
map :: (a -> b) -> (Maybe a -> Maybe b)
map f (Just x) = Just (f x)
map _ Nothing = Nothing
instance Monad Maybe where
return :: t -> Maybe t
return = Just
(=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
f =<< (Just x) = f x
_ =<< Nothing = Nothing
a -> Maybe b
एक परिणाम के लिए लागू होता है केवल अगर Maybe a
एक परिणाम देता है।
newtype Nat = Nat Int
प्राकृतिक संख्याओं को उन पूर्णांकों के रूप में एन्कोड किया जा सकता है जो शून्य से अधिक या उसके बराबर हैं।
toNat :: Int -> Maybe Nat
toNat i | i >= 0 = Just (Nat i)
| otherwise = Nothing
प्राकृतिक संख्या घटाव के तहत बंद नहीं होती हैं।
(-?) :: Nat -> Nat -> Maybe Nat
(Nat n) -? (Nat m) = toNat (n - m)
infixl 6 -?
विकल्प मोनाड अपवाद हैंडलिंग का एक मूल रूप शामिल करता है।
(-? 20) <=< toNat :: Int -> Maybe Nat
सूची प्रकार पर, सूची मोनाद
data [] t = [] | t : [t]
infixr 5 :
और इसके additive मोनॉयड ऑपरेशन "परिशिष्ट"
(++) :: [t] -> [t] -> [t]
(x : xs) ++ ys = x : xs ++ ys
[] ++ ys = ys
infixr 5 ++
परिणामों की एक प्राकृतिक राशि उपज nonlinear संगणना सांकेतिक शब्दों में बदलना ।[t]
0, 1, ...
t
instance Functor [] where
map :: (a -> b) -> ([a] -> [b])
map f (x : xs) = f x : map f xs
map _ [] = []
instance Monad [] where
return :: t -> [t]
return = (: [])
(=<<) :: (a -> [b]) -> [a] -> [b]
f =<< (x : xs) = f x ++ (f =<< xs)
_ =<< [] = []
एक्सटेंशन =<<
concatenates ++
सभी सूचियों [b]
अनुप्रयोगों से उत्पन्न f x
एक Kleisli के तीर a -> [b]
के तत्वों को [a]
एक ही परिणाम सूची में[b]
।
एक सकारात्मक पूर्णांक के उचित विभाजक n
होने दें
divisors :: Integral t => t -> [t]
divisors n = filter (`divides` n) [2 .. n - 1]
divides :: Integral t => t -> t -> Bool
(`divides` n) = (== 0) . (n `rem`)
फिर
forall n. let { f = f <=< divisors } in f n = []
मानद प्रकार वर्ग को परिभाषित करने में, विस्तार के बजाय =<<
, हास्केल मानक अपने फ्लिप, बाइंड ऑपरेटर का उपयोग करता है >>=
।
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
m >> k = m >>= \ _ -> k
{-# INLINE (>>) #-}
return :: a -> m a
return = pure
सादगी की खातिर, यह स्पष्टीकरण प्रकार श्रेणी पदानुक्रम का उपयोग करता है
class Functor f
class Functor m => Monad m
हास्केल में, वर्तमान मानक पदानुक्रम है
class Functor f
class Functor p => Applicative p
class Applicative m => Monad m
क्योंकि न केवल हर मोनाड एक फनकार है, बल्कि हर मादक एक फनकार है और हर मोनाड एक ऐक्यूटिव भी है।
सूची का उपयोग करते हुए मोनाड, अनिवार्य छद्मकोश
for a in (1, ..., 10)
for b in (1, ..., 10)
p <- a * b
if even(p)
yield p
मोटे तौर पर डो ब्लॉक में अनुवाद करता है ,
do a <- [1 .. 10]
b <- [1 .. 10]
let p = a * b
guard (even p)
return p
बराबर मोनाड की समझ ,
[ p | a <- [1 .. 10], b <- [1 .. 10], let p = a * b, even p ]
और अभिव्यक्ति
[1 .. 10] >>= (\ a ->
[1 .. 10] >>= (\ b ->
let p = a * b in
guard (even p) >> -- [ () | even p ] >>
return p
)
)
नेशन बाइंड एक्सप्रेशन के लिए नोटेशन और मोनाड कॉम्प्रिहेंशन सिंटैक्टिक शुगर हैं। बाइंड ऑपरेटर का उपयोग स्थानीय नाम बाध्यकारी परिणामों के लिए किया जाता है।
let x = v in e = (\ x -> e) $ v = v & (\ x -> e)
do { r <- m; c } = (\ r -> c) =<< m = m >>= (\ r -> c)
कहाँ पे
(&) :: a -> (a -> b) -> b
(&) = flip ($)
infixl 0 &
गार्ड फ़ंक्शन को परिभाषित किया गया है
guard :: Additive m => Bool -> m ()
guard True = return ()
guard False = fail
जहां इकाई प्रकार या "खाली टपल"
data () = ()
Additive monads कि समर्थन विकल्प और विफलता एक प्रकार वर्ग का उपयोग करने पर निकाला जा सकता है
class Monad m => Additive m where
fail :: m t
(<|>) :: m t -> m t -> m t
infixl 3 <|>
instance Additive Maybe where
fail = Nothing
Nothing <|> m = m
m <|> _ = m
instance Additive [] where
fail = []
(<|>) = (++)
जहाँ fail
और <|>
एक मोनॉयड बनाते हैंforall k l m.
k <|> fail = k
fail <|> l = l
(k <|> l) <|> m = k <|> (l <|> m)
और fail
additive भिक्षुओं के शून्य तत्व को अवशोषित / नष्ट करना है
_ =<< fail = fail
मैं फ़िन
guard (even p) >> return p
even p
यह सच है, तब गार्ड , स्थानीय स्थिर फ़ंक्शन [()]
की परिभाषा के अनुसार उत्पादन करता है , और>>
\ _ -> return p
परिणाम पर लागू होता है ()
। यदि गलत है, तो गार्ड सूची मोनाद fail
( []
) का उत्पादन करता है , जो क्लेस्ली तीर के लिए कोई परिणाम नहीं देता >>
है, ताकि इसे लागू किया जा सकेp
पर छोड़ दिया जाता है।
बदनाम रूप से, संन्यासी का उपयोग संस्थागत गणना को सांकेतिक करने के लिए किया जाता है।
एक राज्य प्रोसेसर एक फ़ंक्शन है
forall st t. st -> (t, st)
जो एक स्थिति का संक्रमण करता है st
और एक परिणाम देता है t
। राज्य st
में कुछ भी हो सकता है। कुछ नहीं, झंडा, गिनती, सरणी, संभाल, मशीन, दुनिया।
राज्य प्रोसेसर का प्रकार आमतौर पर कहा जाता है
type State st t = st -> (t, st)
राज्य प्रोसेसर इकाई kinded है * -> *
functor State st
। राज्य प्रोसेसर मोनाड के क्लेली तीर कार्य हैं
forall st a b. a -> (State st) b
कैनोनिकल हास्केल में, राज्य प्रोसेसर मोनाड का आलसी संस्करण परिभाषित किया गया है
newtype State st t = State { stateProc :: st -> (t, st) }
instance Functor (State st) where
map :: (a -> b) -> ((State st) a -> (State st) b)
map f (State p) = State $ \ s0 -> let (x, s1) = p s0
in (f x, s1)
instance Monad (State st) where
return :: t -> (State st) t
return x = State $ \ s -> (x, s)
(=<<) :: (a -> (State st) b) -> (State st) a -> (State st) b
f =<< (State p) = State $ \ s0 -> let (x, s1) = p s0
in stateProc (f x) s1
एक राज्य प्रोसेसर एक प्रारंभिक राज्य की आपूर्ति द्वारा चलाया जाता है:
run :: State st t -> st -> (t, st)
run = stateProc
eval :: State st t -> st -> t
eval = fst . run
exec :: State st t -> st -> st
exec = snd . run
राज्य पहुँच पुरातन द्वारा प्रदान की जाती है get
और put
, अधिक अमूर्त के तरीकों स्टेटफुल monads:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Monad m => Stateful m st | m -> st where
get :: m st
put :: st -> m ()
m -> st
भिक्षु पर राज्य प्रकार की कार्यात्मक निर्भरता की घोषणा करता है ; एक है कि , उदाहरण के लिए, राज्य प्रकार का निर्धारण हो जाएगा विशिष्ट।st
m
State t
t
instance Stateful (State st) st where
get :: State st st
get = State $ \ s -> (s, s)
put :: st -> State st ()
put s = State $ \ _ -> ((), s)
इकाई प्रकार के साथ void
C में समान रूप से उपयोग किया जाता है।
modify :: Stateful m st => (st -> st) -> m ()
modify f = do
s <- get
put (f s)
gets :: Stateful m st => (st -> t) -> m t
gets f = do
s <- get
return (f s)
gets
अक्सर रिकॉर्ड फ़ील्ड एक्सेसर्स के साथ उपयोग किया जाता है।
चर सूत्रण के बराबर राज्य मोनाद
let s0 = 34
s1 = (+ 1) s0
n = (* 12) s1
s2 = (+ 7) s1
in (show n, s2)
जहां s0 :: Int
, समान रूप से पारदर्शी पारदर्शी है, लेकिन असीम रूप से अधिक सुरुचिपूर्ण और व्यावहारिक है
(flip run) 34
(do
modify (+ 1)
n <- gets (* 12)
modify (+ 7)
return (show n)
)
modify (+ 1)
State Int ()
इसके समान प्रभाव को छोड़कर, प्रकार की गणना है return ()
।
(flip run) 34
(modify (+ 1) >>
gets (* 12) >>= (\ n ->
modify (+ 7) >>
return (show n)
)
)
सहानुभूति के मोनड कानून के संदर्भ में लिखा जा सकता है >>=
forall m f g.
(m >>= f) >>= g = m >>= (\ x -> f x >>= g)
या
do { do { do {
r1 <- do { x <- m; r0 <- m;
r0 <- m; = do { = r1 <- f r0;
f r0 r1 <- f x; g r1
}; g r1 }
g r1 }
} }
अभिव्यक्ति-उन्मुख प्रोग्रामिंग (जैसे जंग) में, एक ब्लॉक का अंतिम बयान इसकी उपज का प्रतिनिधित्व करता है। बाइंड ऑपरेटर को कभी-कभी "प्रोग्रामेबल अर्धविराम" कहा जाता है।
संरचित अनिवार्य प्रोग्रामिंग से Iteration control संरचना प्राइमेटरी मोनोडिक रूप से अनुकरण की जाती है
for :: Monad m => (a -> m b) -> [a] -> m ()
for f = foldr ((>>) . f) (return ())
while :: Monad m => m Bool -> m t -> m ()
while c m = do
b <- c
if b then m >> while c m
else return ()
forever :: Monad m => m t
forever m = m >> forever m
data World
I / O वर्ल्ड स्टेट प्रोसेसर मोनाड शुद्ध हास्केल और वास्तविक दुनिया का एक मेल है, जो कार्यात्मक संप्रदाय और अनिवार्य परिचालन शब्दार्थ का है। वास्तविक सख्त कार्यान्वयन का एक नजदीकी एनालॉग:
type IO t = World -> (t, World)
बातचीत को अशुद्ध आदिमियों द्वारा सुगम बनाया गया है
getChar :: IO Char
putChar :: Char -> IO ()
readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()
hSetBuffering :: Handle -> BufferMode -> IO ()
hTell :: Handle -> IO Integer
. . . . . .
कोड का उपयोग करने वाली अशुद्धता, IO
स्थायी रूप से टाइप सिस्टम द्वारा प्रोटोकॉल है। क्योंकि पवित्रता भयानक है, इसमें क्या होता है IO
, इसमें रहता है IO
।
unsafePerformIO :: IO t -> t
या, कम से कम, चाहिए।
हास्केल कार्यक्रम का प्रकार हस्ताक्षर
main :: IO ()
main = putStrLn "Hello, World!"
तक फैलता है
World -> ((), World)
एक फ़ंक्शन जो एक दुनिया को बदल देता है।
जिस श्रेणी की वस्तुएं हास्केल प्रकार की होती हैं और हस्केल प्रकारों के बीच कार्य करता है, वह श्रेणी "तेज और ढीली" है Hask
।
एक फ़नकार T
एक श्रेणी से एक श्रेणी के C
लिए एक मानचित्रण है D
; में प्रत्येक वस्तु के लिए C
में एक वस्तुD
Tobj : Obj(C) -> Obj(D)
f :: * -> *
और में प्रत्येक आकारिता के लिए C
में एक आकारिताD
Tmor : HomC(X, Y) -> HomD(Tobj(X), Tobj(Y))
map :: (a -> b) -> (f a -> f b)
जहाँ X
, Y
में वस्तुएँ हैं C
। HomC(X, Y)
है समरूपता वर्ग सभी morphisms की X -> Y
में C
। फ़नकार को मॉर्फ़िज़्म आइडेंटिटी और कंपोज़िशन को संरक्षित करना चाहिए, जिसमें "स्ट्रक्चर" C
इन है D
।
Tmor Tobj
T(id) = id : T(X) -> T(X) Identity
T(f) . T(g) = T(f . g) : T(X) -> T(Z) Composition
एक श्रेणी की क्लेइस्ली श्रेणीC
क्लेइस्ली ट्रिपल द्वारा दी गई है
<T, eta, _*>
एंडोफूनक्टर का
T : C -> C
( f
), एक पहचान आकारिकी eta
( return
), और एक विस्तार ऑपरेटर *
( =<<
)।
प्रत्येक क्लेली मॉर्फिज़्म में Hask
f : X -> T(Y)
f :: a -> m b
विस्तार ऑपरेटर द्वारा
(_)* : Hom(X, T(Y)) -> Hom(T(X), T(Y))
(=<<) :: (a -> m b) -> (m a -> m b)
Hask
क्लेइस्ली श्रेणी में एक रूपवाद दिया जाता है
f* : T(X) -> T(Y)
(f =<<) :: m a -> m b
क्लेस्ली श्रेणी में रचना .T
विस्तार के संदर्भ में दी गई है
f .T g = f* . g : X -> T(Z)
f <=< g = (f =<<) . g :: a -> m c
और श्रेणी स्वयंसिद्धों को संतुष्ट करता है
eta .T g = g : Y -> T(Z) Left identity
return <=< g = g :: b -> m c
f .T eta = f : Z -> T(U) Right identity
f <=< return = f :: c -> m d
(f .T g) .T h = f .T (g .T h) : X -> T(U) Associativity
(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d
जो, तुल्यता परिवर्तनों को लागू करना
eta .T g = g
eta* . g = g By definition of .T
eta* . g = id . g forall f. id . f = f
eta* = id forall f g h. f . h = g . h ==> f = g
(f .T g) .T h = f .T (g .T h)
(f* . g)* . h = f* . (g* . h) By definition of .T
(f* . g)* . h = f* . g* . h . is associative
(f* . g)* = f* . g* forall f g h. f . h = g . h ==> f = g
विस्तार के संदर्भ में विहित रूप से दिए गए हैं
eta* = id : T(X) -> T(X) Left identity
(return =<<) = id :: m t -> m t
f* . eta = f : Z -> T(U) Right identity
(f =<<) . return = f :: c -> m d
(f* . g)* = f* . g* : T(X) -> T(Z) Associativity
(((f =<<) . g) =<<) = (f =<<) . (g =<<) :: m a -> m c
मोनाड्स को क्लेसीलियन एक्सटेंशन के नहीं, बल्कि mu
प्रोग्रामिंग में एक प्राकृतिक परिवर्तन के रूप में परिभाषित किया जा सकता है join
। एक मोनाड को mu
एक श्रेणी के एक ट्रिपल के रूप में परिभाषित किया जाता है C
, एक एंडोफ़नक्टर का
T : C -> C
f :: * -> *
और दो प्राकृतिक ट्रांसफॉर्मेशन
eta : Id -> T
return :: t -> f t
mu : T . T -> T
join :: f (f t) -> f t
समतुल्यता को संतुष्ट करना
mu . T(mu) = mu . mu : T . T . T -> T . T Associativity
join . map join = join . join :: f (f (f t)) -> f t
mu . T(eta) = mu . eta = id : T -> T Identity
join . map return = join . return = id :: f t -> f t
मोनड प्रकार वर्ग तब परिभाषित किया गया है
class Functor m => Monad m where
return :: t -> m t
join :: m (m t) -> m t
mu
विकल्प सनद के विहित कार्यान्वयन:
instance Monad Maybe where
return = Just
join (Just m) = m
join Nothing = Nothing
concat
समारोह
concat :: [[a]] -> [a]
concat (x : xs) = x ++ concat xs
concat [] = []
है join
सूची इकाई की।
instance Monad [] where
return :: t -> [t]
return = (: [])
(=<<) :: (a -> [b]) -> ([a] -> [b])
(f =<<) = concat . map f
के क्रियान्वयन join
तुल्यता का उपयोग कर विस्तार रूप से अनुवाद किया जा सकता
mu = id* : T . T -> T
join = (id =<<) :: m (m t) -> m t
mu
एक्सटेंशन फॉर्म से रिवर्स ट्रांसलेशन द्वारा दिया गया है
f* = mu . T(f) : T(X) -> T(Y)
(f =<<) = join . map f :: m a -> m b
साइमन एल पेटन जोन्स, फिलिप वाडलर: इंपीरियल फंक्शनल प्रोग्रामिंग
जोनाथन एमडी हिल, कीथ क्लार्क: के लिए श्रेणी सिद्धांत, श्रेणी सिद्धांत monads एक परिचय, और कार्यात्मक प्रोग्रामिंग करने के लिए उनके रिश्ते '
यूजेनियो मोगी: गणना और मठों की धारणा
लेकिन प्रोग्रामिंग के लिए किसी सिद्धांत का इतना सार क्यों होना चाहिए?
उत्तर सरल है: कंप्यूटर वैज्ञानिकों के रूप में, हम अमूर्तता को महत्व देते हैं ! जब हम किसी सॉफ़्टवेयर घटक के लिए इंटरफ़ेस डिज़ाइन करते हैं, तो हम चाहते हैं कि यह कार्यान्वयन के बारे में जितना संभव हो उतना कम प्रकट हो। हम कार्यान्वयन को कई विकल्पों, एक ही 'अवधारणा' के कई अन्य 'उदाहरणों' के साथ बदलने में सक्षम होना चाहते हैं। जब हम कई प्रोग्राम पुस्तकालयों में एक सामान्य इंटरफ़ेस डिज़ाइन करते हैं, तो यह और भी महत्वपूर्ण है कि हम जो इंटरफ़ेस चुनते हैं, उसमें कई प्रकार के कार्यान्वयन हैं। यह मोनड अवधारणा की व्यापकता है जिसे हम बहुत अधिक महत्व देते हैं, ऐसा इसलिए है क्योंकि श्रेणी सिद्धांत इतना सार है कि इसकी अवधारणा प्रोग्रामिंग के लिए बहुत उपयोगी है।
यह, शायद ही आश्चर्य की बात है, कि हम नीचे प्रस्तुत भिक्षुओं के सामान्यीकरण का भी श्रेणी सिद्धांत से गहरा संबंध है। लेकिन हम इस बात पर जोर देते हैं कि हमारा उद्देश्य बहुत व्यावहारिक है: यह 'श्रेणी सिद्धांत को लागू करने' के लिए नहीं है, यह कॉम्बिनेटर पुस्तकालयों की संरचना के लिए एक अधिक सामान्य तरीका खोजना है। यह बस हमारा सौभाग्य है कि गणितज्ञों ने हमारे लिए बहुत काम किया है!
से तीर को Generalising monads जॉन ह्यूजेस द्वारा
दुनिया को एक और मोनाड ब्लॉग पोस्ट की आवश्यकता है, लेकिन मुझे लगता है कि यह जंगली में मौजूदा साधुओं की पहचान करने में उपयोगी है।
ऊपर Sierpinski त्रिभुज नामक एक भग्न है, एकमात्र भग्न जिसे मैं आकर्षित करना याद कर सकता हूं। फ्रैक्टल्स उपरोक्त त्रिभुज की तरह स्वयं-समान संरचना है, जिसमें भागों पूरे के समान हैं (इस मामले में मूल त्रिकोण के रूप में आधा पैमाने पर)।
मोनाड भग्न हैं। एक मौद्रिक डेटा संरचना को देखते हुए, इसके मूल्यों को डेटा संरचना का एक और मूल्य बनाने के लिए बनाया जा सकता है। यही कारण है कि यह प्रोग्रामिंग के लिए उपयोगी है, और यही कारण है कि यह कई स्थितियों में होता है।
http://code.google.com/p/monad-tutorial/ वास्तव में इस प्रश्न का समाधान करने के लिए प्रगति पर काम कर रहा है।
नीचे " {| a |m}
" मानदंड डेटा के कुछ टुकड़े का प्रतिनिधित्व करते हैं। एक डेटा प्रकार जो विज्ञापन देता है a
:
(I got an a!)
/
{| a |m}
समारोह, f
जानता है कि कैसे एक सनक बनाने के लिए, अगर केवल यह एक था a
:
(Hi f! What should I be?)
/
(You?. Oh, you'll be /
that data there.) /
/ / (I got a b.)
| -------------- |
| / |
f a |
|--later-> {| b |m}
यहां हम फ़ंक्शन देखते हैं, f
एक मोनाड का मूल्यांकन करने की कोशिश करते हैं, लेकिन फटकार लगाते हैं।
(Hmm, how do I get that a?)
o (Get lost buddy.
o Wrong type.)
o /
f {| a |m}
Funtion, का उपयोग करके f
निकालने का एक तरीका ढूँढता है ।a
>>=
(Muaahaha. How you
like me now!?)
(Better.) \
| (Give me that a.)
(Fine, well ok.) |
\ |
{| a |m} >>= f
कम ही f
जानता है, सन्यासी और >>=
मिलीभगत में हैं।
(Yah got an a for me?)
(Yeah, but hey |
listen. I got |
something to |
tell you first |
...) \ /
| /
{| a |m} >>= f
लेकिन वे वास्तव में क्या बात करते हैं? ठीक है, यह मठ पर निर्भर करता है। केवल सार में बात करना सीमित उपयोग है; आपके पास समझ बढ़ाने के लिए विशेष साधुओं के साथ कुछ अनुभव होना चाहिए।
उदाहरण के लिए, डेटा प्रकार हो सकता है
data Maybe a = Nothing | Just a
एक सनक उदाहरण है जो निम्नलिखित की तरह काम करेगा ...
जिसमें, यदि मामला है Just a
(Yah what is it?)
(... hm? Oh, |
forget about it. |
Hey a, yr up.) |
\ |
(Evaluation \ |
time already? \ |
Hows my hair?) | |
| / |
| (It's |
| fine.) /
| / /
{| a |m} >>= f
लेकिन के मामले के लिए Nothing
(Yah what is it?)
(... There |
is no a. ) |
| (No a?)
(No a.) |
| (Ok, I'll deal
| with this.)
\ |
\ (Hey f, get lost.)
\ | ( Where's my a?
\ | I evaluate a)
\ (Not any more |
\ you don't. |
| We're returning
| Nothing.) /
| | /
| | /
| | /
{| a |m} >>= f (I got a b.)
| (This is \
| such a \
| sham.) o o \
| o|
|--later-> {| b |m}
तो हो सकता है कि मोनाड एक संगणना को जारी रखने देता है यदि उसमें वास्तव में a
यह विज्ञापित होता है, लेकिन यदि यह नहीं होता है तो अभिकलन को निरस्त कर देता है। परिणाम, हालांकि अभी भी एक मानद डेटा का टुकड़ा है, हालांकि आउटपुट नहीं हैf
। इस कारण से, शायद मोनड का उपयोग विफलता के संदर्भ का प्रतिनिधित्व करने के लिए किया जाता है।
अलग-अलग संन्यासी अलग व्यवहार करते हैं। सूचियाँ अन्य प्रकार के डेटा हैं, जिसमें मोनडिक उदाहरण हैं। वे निम्नलिखित की तरह व्यवहार करते हैं:
(Ok, here's your a. Well, its
a bunch of them, actually.)
|
| (Thanks, no problem. Ok
| f, here you go, an a.)
| |
| | (Thank's. See
| | you later.)
| (Whoa. Hold up f, |
| I got another |
| a for you.) |
| | (What? No, sorry.
| | Can't do it. I
| | have my hands full
| | with all these "b"
| | I just made.)
| (I'll hold those, |
| you take this, and /
| come back for more /
| when you're done /
| and we'll do it /
| again.) /
\ | ( Uhhh. All right.)
\ | /
\ \ /
{| a |m} >>= f
इस मामले में, फ़ंक्शन जानता था कि यह इनपुट से एक सूची कैसे बना सकता है, लेकिन यह नहीं जानता कि अतिरिक्त इनपुट और अतिरिक्त सूचियों के साथ क्या करना है। बाँध >>=
, f
कई outputs के संयोजन से मदद की । मैं इस उदाहरण को यह दिखाने के लिए शामिल करता हूं कि >>=
निकालने के लिए ज़िम्मेदार होने a
के बावजूद, इसके अंतिम बाउंड आउटपुट तक भी पहुंच है f
। वास्तव में, यह कभी भी किसी को नहीं निकालेगा a
जब तक कि यह पता न हो कि आखिरकार उत्पादन का एक ही प्रकार का संदर्भ है।
अन्य संन्यासी हैं जो विभिन्न संदर्भों का प्रतिनिधित्व करने के लिए उपयोग किए जाते हैं। यहाँ कुछ और के लक्षण हैं। IO
इकाई वास्तव में एक नहीं है a
, लेकिन यह एक आदमी को जानता है और कहा कि मिल जाएगा a
आप के लिए। State st
इकाई की एक गुप्त गुप्त कोष है st
कि यह करने के लिए पारित करेंगे f
टेबल के नीचे है, भले ही f
सिर्फ एक के लिए पूछ आया a
। Reader r
इकाई के समान है State st
, हालांकि यह केवल की सुविधा देता है, f
पर देखो r
।
इस सब में बिंदु यह है कि किसी भी प्रकार का डेटा जो खुद को मोनाड घोषित करता है, वह मोनाड से मूल्य निकालने के आसपास किसी तरह का संदर्भ घोषित कर रहा है। इस सब से बड़ा फायदा? खैर, यह आसान है कि किसी प्रकार के संदर्भ के साथ गणना को काउच किया जाए। यह गड़बड़ हो सकता है, हालांकि, जब एक साथ कई संदर्भ लादेन गणना करते हैं। मोनाद संचालन संदर्भ की बातचीत को हल करने का ध्यान रखता है ताकि प्रोग्रामर को न हो।
ध्यान दें, कि >>=
स्वायत्तता से कुछ दूर ले जाकर आसानी से एक गड़बड़ का उपयोग करता है f
। Nothing
उदाहरण के लिए, उपरोक्त मामले में , f
अब यह तय नहीं किया जाता है कि मामले में क्या करना है Nothing
; इसमें एन्कोड किया गया है >>=
। यह व्यापार बंद है। यदि यह जरूरी हो गया था के लिए f
तय करने के लिए क्या करने के मामले में क्या करना Nothing
है, तो f
से एक समारोह किया जाना चाहिए था Maybe a
करने के लिए Maybe b
। इस मामले में, Maybe
एक सन्यासी होना अप्रासंगिक है।
ध्यान दें, हालांकि, कभी-कभी एक डेटा प्रकार यह निर्यात करने वालों को निर्यात नहीं करता है (आप पर ध्यान केंद्रित करते हुए IO), और यदि हम विज्ञापित मूल्य के साथ काम करना चाहते हैं तो हमारे पास बहुत कम विकल्प हैं लेकिन इसके साथ काम करना है।
एक सनक एक ऐसी चीज है जिसका उपयोग वस्तुओं को बदलने के लिए किया जाता है जिनकी बदलती स्थिति होती है। यह अक्सर उन भाषाओं में सामना किया जाता है जो अन्यथा आपको परिवर्तनीय स्थिति (जैसे, हास्केल) की अनुमति नहीं देते हैं।
एक उदाहरण I / O फ़ाइल के लिए होगा।
आप फ़ाइल I / O के लिए एक सनक का उपयोग करने में सक्षम होंगे जो बदलते राज्य की प्रकृति को केवल उस कोड को अलग करने के लिए जो मोनाड का उपयोग करता है। मोनाड के अंदर का कोड मोनाड के बाहर की दुनिया की बदलती स्थिति को प्रभावी ढंग से अनदेखा कर सकता है - इससे आपके कार्यक्रम के समग्र प्रभाव के बारे में तर्क करना बहुत आसान हो जाता है।