आइए पहले अमूर्त अवधारणाओं को सीखने और उनमें से विशिष्ट उदाहरणों को सीखने के बीच अंतर करें।
आप सभी विशिष्ट उदाहरणों की अनदेखी करने के लिए बहुत दूर नहीं जा रहे हैं, साधारण कारण के लिए कि वे पूरी तरह से सर्वव्यापी हैं। वास्तव में, अमूर्त बड़े हिस्से में मौजूद हैं क्योंकि वे उन चीजों को एकीकृत करते हैं जो आप विशिष्ट उदाहरणों के साथ वैसे भी कर रहे होंगे।
दूसरी ओर, स्वयं सार, निश्चित रूप से उपयोगी हैं , लेकिन वे तुरंत आवश्यक नहीं हैं। आप पूरी तरह से और सीधे सीधे विभिन्न प्रकारों का उपयोग करते हुए बहुत दूर तक की अनदेखी कर सकते हैं। आप उन्हें अंततः समझना चाहते हैं, लेकिन आप हमेशा बाद में वापस आ सकते हैं। वास्तव में, मैं लगभग गारंटी दे सकता हूं कि यदि आप ऐसा करते हैं, तो जब आप इसे वापस करते हैं तो आप अपने माथे को थप्पड़ मारेंगे और आश्चर्य करेंगे कि आपने सुविधाजनक सामान्य-प्रयोजन साधनों का उपयोग करने के बजाय चीजों को कठिन तरीके से करने में अपना सारा समय क्यों लगाया।
Maybe a
एक उदाहरण के रूप में लें । यह सिर्फ एक डेटा प्रकार है:
data Maybe a = Just a | Nothing
यह सब लेकिन स्व-दस्तावेजीकरण है; यह एक वैकल्पिक मूल्य है। या तो आपके पास कुछ प्रकार का "बस" है a
, या आपके पास कुछ भी नहीं है। मान लें कि आपके पास किसी प्रकार का एक लुकअप फ़ंक्शन है, जो Maybe String
किसी ऐसे String
मान को दर्शाने के लिए लौटाता है जो मौजूद नहीं हो सकता है। तो आप यह देखने के लिए कि यह कौन सा है:
case lookupFunc key of
Just val -> ...
Nothing -> ...
बस इतना ही!
वास्तव में, आपको और कुछ नहीं चाहिए। कोई Functor
s या Monad
रों या कुछ और। वे Maybe a
मूल्यों का उपयोग करने के सामान्य तरीके व्यक्त करते हैं ... लेकिन वे सिर्फ मुहावरे हैं, "डिजाइन पैटर्न", जो भी आप इसे कॉल करना चाहते हैं।
एक जगह जिसे आप वास्तव में पूरी तरह से नहीं बचा सकते हैं IO
, लेकिन यह एक रहस्यमय ब्लैक बॉक्स है, इसलिए यह समझने की कोशिश करने के लायक नहीं है कि इसका अर्थ क्या है Monad
या जो कुछ भी है।
वास्तव में, यहाँ आप सभी के लिए एक धोखा पत्र वास्तव में IO
अब के बारे में जानने की जरूरत है :
यदि किसी चीज़ का प्रकार है IO a
, तो इसका मतलब है कि यह एक ऐसी प्रक्रिया है जो कुछ करती है और एक a
मूल्य निकालती है ।
जब आपके पास do
नोटेशन का उपयोग करके कोड का एक ब्लॉक होता है, तो कुछ इस तरह से लिखना:
do -- ...
inp <- getLine
-- etc...
... का अर्थ है प्रक्रिया को दाईं ओर निष्पादित करना<-
, और परिणाम को बाईं ओर नाम निर्दिष्ट करना।
जबकि अगर आपके पास ऐसा कुछ है:
do -- ...
let x = [foo, bar]
-- etc...
... इसका अर्थ है बाईं ओर के =
नाम के दाईं ओर सादे अभिव्यक्ति (एक प्रक्रिया नहीं) के मूल्य को निर्दिष्ट करना ।
यदि आप इस तरह से एक मूल्य बताए बिना कुछ डालते हैं, जैसे:
do putStrLn "blah blah, fishcakes"
... इसका मतलब है कि एक प्रक्रिया को निष्पादित करना और कुछ भी इसे अनदेखा करना। कुछ प्रक्रियाओं के प्रकार होते हैं IO ()
- ()
प्रकार एक प्लेसहोल्डर की तरह होता है जो कुछ भी नहीं कहता है, इसलिए इसका मतलब है कि प्रक्रिया कुछ करती है और एक मूल्य वापस नहीं करती है। void
अन्य भाषाओं में एक फ़ंक्शन की तरह ।
एक ही प्रक्रिया को एक से अधिक बार निष्पादित करना अलग परिणाम दे सकता है; इस तरह का विचार है। यही कारण है कि IO
मूल्य से "हटाने" का कोई तरीका नहीं है , क्योंकि कुछ IO
मूल्य नहीं है, यह एक मूल्य प्राप्त करने की एक प्रक्रिया है।
किसी do
ब्लॉक में अंतिम पंक्ति बिना किसी असाइनमेंट के एक सादे प्रक्रिया होनी चाहिए, जहां उस प्रक्रिया का रिटर्न मान पूरे ब्लॉक के लिए रिटर्न वैल्यू बन जाता है। यदि आप चाहते हैं कि रिटर्न वैल्यू पहले से ही दिए गए कुछ मूल्य का उपयोग करें, तो return
फ़ंक्शन एक सादा मूल्य लेता है और आपको एक नो-ऑप प्रक्रिया देता है जो उस मूल्य को वापस करता है।
इसके अलावा, वहाँ के बारे में कुछ खास नहीं है IO
; ये प्रक्रियाएं वास्तव में स्वयं सादा मूल्य हैं, और आप उन्हें चारों ओर से गुजर सकते हैं और उन्हें विभिन्न तरीकों से जोड़ सकते हैं। यह केवल तभी होता है जब उन्हें एक do
ब्लॉक में निष्पादित किया जाता है जिसे कहीं न कहीं main
वे कहते हैं कि वे कुछ भी करते हैं।
तो, कुछ इस तरह से उबाऊ, टकसाली उदाहरण कार्यक्रम:
hello = do putStrLn "What's your name?"
name <- getLine
let msg = "Hi, " ++ name ++ "!"
putStrLn msg
return name
... आप इसे एक अनिवार्य कार्यक्रम की तरह पढ़ सकते हैं। हम नाम की एक प्रक्रिया को परिभाषित कर रहे हैं hello
। जब निष्पादित किया जाता है, तो पहले यह आपके नाम से पूछते हुए एक संदेश मुद्रित करने के लिए एक प्रक्रिया निष्पादित करता है; इसके बाद यह एक प्रक्रिया निष्पादित करता है जो इनपुट की एक पंक्ति को पढ़ता है, और परिणाम को असाइन करता है name
; तब यह नाम को एक अभिव्यक्ति प्रदान करता है msg
; तब यह संदेश प्रिंट करता है; फिर यह पूरे ब्लॉक के परिणाम के रूप में उपयोगकर्ता का नाम लौटाता है। चूंकि name
ए है String
, इसका मतलब यह है कि hello
प्रक्रिया एक है जो रिटर्न करती है String
, इसलिए इसका प्रकार है IO String
। और अब आप इस प्रक्रिया को कहीं और निष्पादित कर सकते हैं, जैसे यह निष्पादित करता है getLine
।
पफ, मोनाड्स। उन्हें कौन चाहिए?