यदि कार्यात्मक प्रोग्रामिंग भाषाएं किसी भी राज्य को नहीं बचा सकती हैं, तो वे कुछ सरल सामान कैसे करते हैं जैसे कि उपयोगकर्ता से इनपुट पढ़ना (मेरा मतलब है कि वे इसे "कैसे स्टोर करें"), या उस मामले के लिए कोई डेटा संग्रहीत कर रहे हैं?
जब आप एकत्रित हुए, कार्यात्मक प्रोग्रामिंग में स्थिति नहीं है - लेकिन इसका मतलब यह नहीं है कि यह डेटा संग्रहीत नहीं कर सकता है। अंतर यह है कि अगर मैं एक (हास्केल) बयान लिखता हूं
let x = func value 3.14 20 "random"
in ...
मुझे इसकी गारंटी है कि मूल्य x
हमेशा में समान होता है ...
: कुछ भी संभवतः इसे बदल नहीं सकता है। इसी तरह, अगर मेरे पास कोई फ़ंक्शन f :: String -> Integer
(स्ट्रिंग लेने और एक पूर्णांक वापस करने वाला एक फ़ंक्शन) है, तो मुझे यकीन है कि f
इसके तर्क को संशोधित नहीं करेगा, या किसी भी वैश्विक चर को बदल देगा, या किसी फ़ाइल पर डेटा लिख सकता है, और इसी तरह। जैसा कि ऊपर टिप्पणी में sepp2k ने कहा, यह गैर-परिवर्तनशीलता कार्यक्रमों के बारे में तर्क करने के लिए वास्तव में मददगार है: आप ऐसे कार्य लिखते हैं जो आपके डेटा को मोड़ते हैं, टटोलते हैं, और नई प्रतियां लौटाते हैं, ताकि आप उन्हें एक साथ जोड़ सकें, और आप यह सुनिश्चित कर सकते हैं कि कोई भी नहीं उन फ़ंक्शन कॉल में से कुछ भी "हानिकारक" कर सकते हैं। आप जानते हैं कि x
यह हमेशा होता है x
, और आपको चिंता करने की ज़रूरत नहीं है कि किसी ने x := foo bar
घोषणा के बीच में कहीं लिखा हैx
और इसका उपयोग, क्योंकि यह असंभव है।
अब, क्या होगा यदि मैं किसी उपयोगकर्ता से इनपुट पढ़ना चाहता हूं? जैसा कि केनीटीएम ने कहा, विचार यह है कि एक अशुद्ध कार्य एक शुद्ध कार्य है जो पूरी दुनिया को एक तर्क के रूप में पारित करता है, और इसके परिणाम और दुनिया दोनों को वापस करता है। बेशक, आप वास्तव में ऐसा नहीं करना चाहते हैं: एक बात के लिए, यह बहुत ही भद्दा है, और दूसरे के लिए, अगर मैं उसी विश्व वस्तु का पुन: उपयोग करता हूं तो क्या होगा? तो यह किसी भी तरह सार हो जाता है। हास्केल इसे IO प्रकार के साथ संभालता है:
main :: IO ()
main = do str <- getLine
let no = fst . head $ reads str :: Integer
...
यह हमें बताता है कि main
एक IO कार्रवाई है जो कुछ भी नहीं लौटाती है; इस क्रिया को निष्पादित करने का अर्थ है हास्केल कार्यक्रम को चलाना। नियम यह है कि IO प्रकार IO क्रिया से बच नहीं सकते हैं; इस संदर्भ में, हम उस क्रिया का उपयोग करते हैं do
। इस प्रकार, getLine
एक रिटर्न देता है IO String
, जिसे दो तरीकों से सोचा जा सकता है: पहला, एक कार्रवाई के रूप में, जो जब चलता है, एक स्ट्रिंग पैदा करता है; दूसरा, एक स्ट्रिंग के रूप में जो आईओ द्वारा "दागी" है क्योंकि यह स्पष्ट रूप से प्राप्त किया गया था। पहला अधिक सही है, लेकिन दूसरा अधिक सहायक हो सकता है। <-
लेता हैString
से बाहर IO String
में और यह भंडार str
-लेकिन के बाद से हम एक आईओ कार्रवाई में कर रहे हैं, हम इसे बैक अप लेने रैप करने के लिए, तो यह "भागने" नहीं कर सकते हैं होगा। अगली पंक्ति एक पूर्णांक पढ़ने की कोशिश करती है ( reads
) और पहला सफल मैच पकड़ती है (fst . head
); यह सब शुद्ध है (कोई IO नहीं), इसलिए हम इसे एक नाम देते हैं let no = ...
। हम तो no
और str
में दोनों का उपयोग कर सकते हैं ...
। हम इस प्रकार अशुद्ध डेटा संग्रहीत किया है (से getLine
में str
) और शुद्ध डेटा ( let no = ...
)।
IO के साथ काम करने के लिए यह तंत्र बहुत शक्तिशाली है: यह आपको अपने प्रोग्राम के शुद्ध, एल्गोरिथम भाग को अशुद्ध, उपयोगकर्ता-सहभागिता पक्ष से अलग करने देता है, और इस प्रकार के स्तर पर लागू करता है। तुम्हारीminimumSpanningTree
फ़ंक्शन संभवतः आपके कोड में कहीं और कुछ नहीं बदल सकता है, या आपके उपयोगकर्ता को संदेश लिख सकता है, और इसी तरह। यह सुरक्षित है।
यह आपको हास्केल में IO का उपयोग करने के लिए जानना चाहिए; अगर आप चाहते हैं, तो आप यहाँ रुक सकते हैं। लेकिन अगर आप समझना चाहते हैं कि क्यों काम करता है, तो पढ़ते रहें। (और ध्यान दें कि यह सामान हास्केल के लिए विशिष्ट होगा — अन्य भाषाएँ एक अलग कार्यान्वयन चुन सकती हैं।)
तो यह शायद एक धोखा की तरह लग रहा था, किसी तरह शुद्ध हास्केल में अशुद्धता को जोड़ना। लेकिन ऐसा नहीं है - यह पता चलता है कि हम IO प्रकार को पूरी तरह से शुद्ध हास्केल के भीतर लागू कर सकते हैं (जब तक हम दिए जाते हैं RealWorld
)। विचार यह है: एक IO क्रिया IO type
एक फ़ंक्शन के समान है RealWorld -> (type, RealWorld)
, जो वास्तविक दुनिया को लेता है और दोनों प्रकार की वस्तु type
और संशोधित लौटाता है RealWorld
। हम तब कुछ कार्यों को परिभाषित करते हैं ताकि हम बिना पागल हुए इस प्रकार का उपयोग कर सकें:
return :: a -> IO a
return a = \rw -> (a,rw)
(>>=) :: IO a -> (a -> IO b) -> IO b
ioa >>= fn = \rw -> let (a,rw') = ioa rw in fn a rw'
पहला हमें IO क्रियाओं के बारे में बात करने की अनुमति देता है जो कुछ भी नहीं करते हैं: return 3
एक IO क्रिया है जो वास्तविक दुनिया को क्वेरी नहीं करती है और सिर्फ रिटर्न देती है 3
। >>=
ऑपरेटर, उच्चारण "बाँध", हमें आईओ कार्यों को चलाने के लिए अनुमति देते हैं। यह IO क्रिया से मान निकालता है, फ़ंक्शन के माध्यम से इसे और वास्तविक दुनिया को पास करता है, और परिणामी IO क्रिया देता है। ध्यान दें कि >>=
हमारे नियम को लागू करता है कि IO क्रियाओं के परिणामों को कभी भी बचने की अनुमति नहीं है।
फिर हम main
फ़ंक्शन अनुप्रयोगों के निम्नलिखित सामान्य सेट में उपरोक्त को बदल सकते हैं :
main = getLine >>= \str -> let no = (fst . head $ reads str :: Integer) in ...
हस्केल रनटाइम जंप main
प्रारंभिक के साथ शुरू होता है RealWorld
, और हम सेट कर रहे हैं! सब कुछ शुद्ध है, यह सिर्फ एक फैंसी वाक्यविन्यास है।
[ संपादित करें: जैसा कि @Conal बताता है , यह वास्तव में वह नहीं है जो हास्केल आईओ करने के लिए उपयोग करता है। यह मॉडल टूटता है यदि आप एक संगोष्ठी जोड़ते हैं, या वास्तव में दुनिया के लिए किसी भी तरह से एक IO कार्रवाई के बीच में बदल जाते हैं, इसलिए हास्केल के लिए इस मॉडल का उपयोग करना असंभव होगा। यह केवल अनुक्रमिक गणना के लिए सटीक है। इस प्रकार, यह हो सकता है कि हास्केल का आईओ थोड़ा चकमा है; यहां तक कि अगर यह नहीं है, यह निश्चित रूप से यह बहुत सुंदर नहीं है। @ कॉनल के अवलोकन के अनुसार, साइमन पेटन-जोन्स ने टैकलिंग द अक्लवर्ड स्क्वाड [pdf] में क्या कहा , धारा 3.1 देखें; वह इन पंक्तियों के साथ एक वैकल्पिक मॉडल को प्रस्तुत कर सकता है, लेकिन फिर इसे अपनी जटिलता के लिए छोड़ देता है और एक अलग तरह का व्यवहार करता है।]
फिर, यह बताता है (बहुत ज्यादा) कैसे IO, और सामान्य रूप से परिवर्तनशीलता, हास्केल में काम करता है; अगर यह सब आप जानना चाहते हैं, तो आप यहां पढ़ना बंद कर सकते हैं। यदि आप सिद्धांत की एक अंतिम खुराक चाहते हैं, तो पढ़ते रहें - लेकिन याद रखें, इस बिंदु पर, हम वास्तव में आपके प्रश्न से बहुत दूर चले गए हैं!
तो एक आखिरी बात: यह इस संरचना को बदल देता है - एक पैरामीट्रिक प्रकार return
और >>=
- बहुत सामान्य है; यह एक इकाई कहा जाता है, और do
संकेतन, return
और >>=
उनमें से किसी के साथ काम करते हैं। जैसा कि आपने यहां देखा, सनक जादुई नहीं है; यह सब जादुई है कि do
ब्लॉक फ़ंक्शन कॉल में बदल जाते हैं। RealWorld
प्रकार केवल जगह हम किसी भी जादू देखते हैं। []
लिस्ट कंस्ट्रक्टर की तरह टाइप भी मोनड हैं और उनका अशुद्ध कोड से कोई लेना-देना नहीं है।
अब आप जानते हैं कि लगभग एक मठ की अवधारणा के बारे में सब कुछ (कुछ कानूनों को छोड़कर जो संतुष्ट होना चाहिए और औपचारिक गणितीय परिभाषा होनी चाहिए), लेकिन आपके पास अंतर्ज्ञान की कमी है। ऑनलाइन में मोनाड ट्यूटोरियल की एक हास्यास्पद संख्या है; मुझे यह पसंद है , लेकिन आपके पास विकल्प हैं। हालाँकि, यह शायद आपकी मदद नहीं करेगा ; अंतर्ज्ञान प्राप्त करने का एकमात्र वास्तविक तरीका उन्हें उपयोग करने और सही समय पर युगल ट्यूटोरियल पढ़ने के संयोजन के माध्यम से है।
हालांकि, आपको IO को समझने के लिए उस अंतर्ज्ञान की आवश्यकता नहीं है । मठों को पूरी तरह से समझना केक पर आधारित है, लेकिन आप अभी IO का उपयोग कर सकते हैं। मैं आपको पहला main
फ़ंक्शन दिखाने के बाद इसका उपयोग कर सकता था । तुम भी IO कोड के रूप में इलाज कर सकते हैं, क्योंकि यह एक अशुद्ध भाषा में था! लेकिन याद रखें कि एक अंतर्निहित कार्यात्मक प्रतिनिधित्व है: किसी को धोखा नहीं है।
(पुनश्च: लंबाई के बारे में क्षमा करें। मैं थोड़ी दूर तक गया था।)