क्या साइड इफेक्ट्स को पूरी तरह से अकादमिक रूप से संभालने के लिए IO मोनाड पैटर्न का लाभ है?


17

अभी तक एक और एफपी + साइड इफेक्ट्स प्रश्न के लिए खेद है, लेकिन मैं एक मौजूदा एक नहीं ढूंढ सका, जो मेरे लिए इसका उत्तर देता है।

कार्यात्मक प्रोग्रामिंग की मेरी (सीमित) समझ यह है कि राज्य / साइड इफेक्ट्स को कम से कम किया जाना चाहिए और स्टेटलेस लॉजिक से अलग रखा जाना चाहिए।

मैं इसके लिए हास्केल के दृष्टिकोण को भी इकट्ठा करता हूं, आईओ सनक, एक कंटेनर में राज्य के कार्यों को लपेटकर प्राप्त करता है, बाद में निष्पादन के लिए, कार्यक्रम के दायरे से बाहर माना जाता है।

मैं इस पैटर्न को समझने की कोशिश कर रहा हूं, लेकिन वास्तव में यह निर्धारित करने के लिए कि क्या इसे पायथन प्रोजेक्ट में उपयोग करना है, इसलिए यदि हास्सेल स्पेसिफिकेशन से बचना चाहते हैं।

आने वाला क्रूड उदाहरण।

अगर मेरा प्रोग्राम XML फाइल को JSON फाइल में बदलता है:

def main():
    xml_data = read_file('input.xml')  # impure
    json_data = convert(xml_data)  # pure
    write_file('output.json', json_data) # impure

ऐसा करने के लिए प्रभावी रूप से IO मोनाद का दृष्टिकोण नहीं है:

steps = list(
    read_file,
    convert,
    write_file,
)

तब वास्तव में उन कदमों को न बुलाकर जिम्मेदारी से खुद को दूर कर लेना , लेकिन दुभाषिए को ऐसा करने देना?

या कोई और तरीका रखें, यह लिखने जैसा है:

def main():  # pure
    def inner():  # impure
        xml_data = read_file('input.xml')
        json_data = convert(xml_data)
        write_file('output.json', json_data)
    return inner

फिर किसी और से कॉल करने की अपेक्षा करना inner()और कहना कि आपका काम पूरा हो गया main()है क्योंकि वह शुद्ध है।

पूरा कार्यक्रम मूल रूप से IO मोनद में निहित है।

जब कोड वास्तव में निष्पादित होता है , तो फ़ाइल को पढ़ने के बाद सब कुछ उस फ़ाइल की स्थिति पर निर्भर करता है, इसलिए अभी भी समान राज्य से संबंधित कीड़े से पीड़ित होगा जैसा कि अनिवार्य कार्यान्वयन, इसलिए क्या आपने वास्तव में कुछ भी प्राप्त किया है, एक प्रोग्रामर के रूप में जो इसे बनाए रखेगा?

मैं राज्य के व्यवहार को कम करने और अलग करने के लाभ की पूरी तरह से सराहना करता हूं , जो वास्तव में है कि मैंने इस तरह के अनिवार्य संस्करण को संरचित किया है: इनपुट इकट्ठा करें, शुद्ध सामान करें, आउटपुट को थूक दें। उम्मीद है कि convert()पूरी तरह से शुद्ध हो सकता है और cachability, threadsafety, आदि का लाभ उठा सकता है।

मैं यह भी सराहना करता हूं कि विशेष रूप से तुलनीय प्रकारों पर काम करने वाली पाइपलाइनों में मोनैडिक प्रकार उपयोगी हो सकते हैं, लेकिन यह नहीं देखते हैं कि आईओ को जब तक इस तरह की पाइप लाइन में पहले से ही मोनड का उपयोग नहीं करना चाहिए।

क्या आईओ मोनाड पैटर्न लाता है, जो मुझे याद आ रहा है साइड इफेक्ट से निपटने के लिए कुछ अतिरिक्त लाभ है?


1
आपको यह वीडियो देखना चाहिए । भिक्षुओं के चमत्कारों को अंततः श्रेणी सिद्धांत या हास्केल का सहारा लिए बिना प्रकट किया जाता है। यह पता चलता है कि जावास्क्रिप्ट में मोनोएड्स को तुच्छ रूप से व्यक्त किया गया है, और अजाक्स के प्रमुख प्रवर्तकों में से एक हैं। मोनाड अद्भुत हैं। वे सरल चीजें हैं, लगभग तुच्छ रूप से कार्यान्वित की जाती हैं, जिसमें जटिलता को प्रबंधित करने की बहुत शक्ति होती है। लेकिन उन्हें समझना आश्चर्यजनक रूप से कठिन है, और ज्यादातर लोग, एक बार जब वे उस आह-हा पल को, उन्हें दूसरों को समझाने की क्षमता खो देते हैं।
रॉबर्ट हार्वे

अच्छा वीडियो, धन्यवाद। मैंने वास्तव में जेएस इंट्रो से कार्यात्मक प्रोग्रामिंग के लिए इस सामान के बारे में सीखा (फिर एक लाख और पढ़ें ...)। हालाँकि, यह देखने के बाद, मुझे पूरा यकीन है कि मेरा प्रश्न IO सनक के लिए विशिष्ट है, जो कि Crock उस वीडियो में शामिल नहीं है।
स्टु कॉक्स

हम्म ... क्या AJAX को I / O का रूप नहीं माना जाता है?
रॉबर्ट हार्वे

1
ध्यान दें कि mainहास्केल कार्यक्रम का प्रकार है IO ()- एक IO क्रिया। यह वास्तव में एक कार्य नहीं है; यह एक मूल्य है । आपका पूरा कार्यक्रम एक शुद्ध मूल्य है जिसमें निर्देश हैं जो भाषा रनटाइम को बताता है कि उसे क्या करना चाहिए। सभी अशुद्ध सामान (वास्तव में IO क्रिया करना) आपके कार्यक्रम के दायरे से बाहर है।
वायज़ार्ड

आपके उदाहरण में, मौद्रिक भाग वह है जब आप एक संगणना ( read_file) का परिणाम लेते हैं और इसे अगले एक ( write_file) के तर्क के रूप में उपयोग करते हैं । यदि आपके पास केवल स्वतंत्र कार्यों का क्रम था, तो आपको मोनाड की आवश्यकता नहीं होगी।
lortabac 10

जवाबों:


14

पूरा कार्यक्रम मूल रूप से IO मोनद में निहित है।

यही वह जगह है जहां मुझे लगता है कि आप इसे हास्केलर्स के नजरिए से नहीं देख रहे हैं। तो हमारे पास इस तरह का एक कार्यक्रम है:

module Main

main :: IO ()
main = do
  xmlData <- readFile "input.xml"
  let jsonData = convert xmlData
  writeFile "output.json" jsonData

convert :: String -> String
convert xml = ...

मुझे लगता है कि इस पर एक विशिष्ट हास्केलर का ले जाएगा कि convert, शुद्ध भाग:

  1. शायद इस कार्यक्रम का थोक है, और IOभागों की तुलना में कहीं अधिक जटिल है ;
  2. बिना किसी से निपटने के बारे में तर्क और परीक्षण किया जा सकता है IO

इसलिए वे इसे convert"निहित" होने के रूप में नहीं देखते हैं IO, बल्कि इसके रूप में इसे अलग-थलग किया जा रहा है IO। अपने प्रकार से, जो कुछ भी convertकरता है वह कभी भी किसी भी चीज पर निर्भर नहीं होता है जो किसी IOकार्रवाई में होता है ।

जब कोड वास्तव में निष्पादित होता है, तो फ़ाइल को पढ़ने के बाद सब कुछ उस फ़ाइल की स्थिति पर निर्भर करता है, इसलिए अभी भी समान राज्य से संबंधित कीड़े से पीड़ित होगा जैसा कि अनिवार्य कार्यान्वयन, तो क्या आपने वास्तव में कुछ भी प्राप्त किया है, एक प्रोग्रामर के रूप में जो इसे बनाए रखेगा?

मैं कहूंगा कि यह दो चीजों में विभाजित है:

  1. जब प्रोग्राम चलता है, तो फ़ाइल की स्थिति पर निर्भर करने के लिए तर्क का मूल्य convert
  2. लेकिन convertफ़ंक्शन क्या करता है , यह फ़ाइल की स्थिति पर निर्भर नहीं करता है। convertहमेशा एक ही कार्य होता है , भले ही वह अलग-अलग बिंदुओं पर विभिन्न तर्कों के साथ दिया गया हो।

यह कुछ हद तक एक सार बिंदु है, लेकिन यह वास्तव में महत्वपूर्ण है कि हास्केलर का क्या मतलब है जब वे इस बारे में बात करते हैं। आप convertइस तरह से लिखना चाहते हैं कि किसी भी मान्य तर्क को दिया जाए , यह उस तर्क के लिए एक सही परिणाम देगा। जब आप इसे इस तरह से देखते हैं, तो यह तथ्य कि फाइल पढ़ना एक स्टेटफुल ऑपरेशन है, यह समीकरण में दर्ज नहीं होता है; यह सब मायने रखता है कि जो भी तर्क उसे खिलाया जाता है और जहाँ से भी आया है, convertउसे सही ढंग से संभालना चाहिए। और तथ्य यह है कि शुद्धता प्रतिबंधित करती है कि convertइसके इनपुट के साथ क्या कर सकते हैं, उस तर्क को सरल करता है।

इसलिए यदि convertकुछ तर्कों से गलत परिणाम उत्पन्न होते हैं, और readFileइसे ऐसे तर्क देते हैं, तो हम यह नहीं देखते हैं कि राज्य द्वारा पेश किए गए बग के रूप में । यह एक शुद्ध कार्य में एक बग है!


मुझे लगता है कि यह सबसे अच्छा विवरण है (हालांकि दूसरों ने मेरे लिए चीजों को स्पष्ट करने में मदद की), धन्यवाद।
स्टू कॉक्स

क्या यह ध्यान देने योग्य है कि अजगर में भिक्षुओं का उपयोग करने से कम लाभ हो सकता है क्योंकि अजगर के पास केवल एक (स्थिर) प्रकार होता है, और इसलिए किसी भी चीज़ के बारे में कोई गारंटी नहीं देता है?
जे.के.

7

यह सुनिश्चित करना कठिन है कि आप "विशुद्ध रूप से अकादमिक" से क्या मतलब है, लेकिन मुझे लगता है कि इसका जवाब ज्यादातर "नहीं" है।

जैसा कि साइमन पेटन जोन्स द्वारा अवेकवर्ड स्क्वाड का सामना करने के बारे में बताया गया था ( जोरदार तरीके से पढ़ने की सिफारिश की गई थी!), एक प्रकार का मैडिक I / O का मतलब था कि जिस तरह से हास्केल I / O को संभालता था, उसी तरह से वास्तविक समस्याओं को हल करना। अनुरोध और जवाब के साथ सर्वर का उदाहरण पढ़ें, जिसे मैं यहां कॉपी नहीं करूंगा; यह बहुत शिक्षाप्रद है।

हास्केल, पायथन के विपरीत, "शुद्ध" अभिकलन की एक शैली को प्रोत्साहित करता है जिसे इसके प्रकार प्रणाली द्वारा लागू किया जा सकता है। बेशक, आप इस शैली का पालन करने के लिए पायथन में प्रोग्रामिंग करते समय आत्म-अनुशासन का उपयोग कर सकते हैं, लेकिन उन मॉड्यूल के बारे में जो आपने नहीं लिखा था? प्रकार प्रणाली (और सामान्य पुस्तकालयों) से बहुत मदद के बिना, पायदान में I / O शायद कम उपयोगी है। भाषा का दर्शन सिर्फ एक सख्त शुद्ध / अशुद्ध जुदाई को लागू करने के लिए नहीं है।

ध्यान दें कि यह हास्केल और पायथन के विभिन्न दर्शनों के बारे में अधिक बताता है कि शैक्षणिक मैं / ओ कैसे है। मैं इसका इस्तेमाल पायथन के लिए नहीं करूंगा।

एक बात और। तुम कहो:

पूरा कार्यक्रम मूल रूप से IO मोनद में निहित है।

यह सच है कि हास्केल mainफ़ंक्शन "जीवन" में है IO, लेकिन वास्तविक हास्केल कार्यक्रमों को IOजब भी इसकी आवश्यकता नहीं है, उपयोग नहीं करने के लिए प्रोत्साहित किया जाता है। लगभग हर फ़ंक्शन जो आप लिखते हैं, मुझे I / O करने की आवश्यकता नहीं है जो टाइप नहीं होना चाहिए IO

इसलिए मैं आपके अंतिम उदाहरण में कहूंगा कि आप इसे पीछे की ओर ले गए हैं: mainअशुद्ध है (क्योंकि यह फाइलें पढ़ता है और लिखता है) लेकिन मूल कार्य जैसे convertशुद्ध हैं।


3

IO अशुद्ध क्यों है? क्योंकि यह अलग-अलग समय पर अलग-अलग मान लौटा सकता है। समय पर निर्भरता है कि एक तरह से या किसी अन्य के लिए जिम्मेदार होना चाहिए। यह आलसी मूल्यांकन के साथ और भी महत्वपूर्ण है। निम्नलिखित कार्यक्रम पर विचार करें:

main = do  
    putStrLn "Please enter your name"  
    name <- getLine
    putStrLn $ "Hello, " ++ name

बिना आईओ मोनाद के, पहले प्रॉम्प्ट को कभी आउटपुट क्यों मिलेगा? इसके आधार पर कुछ भी नहीं है, इसलिए आलसी मूल्यांकन का मतलब है कि इसकी कभी मांग नहीं होगी। इनपुट पढ़ने से पहले आउटपुट होने के लिए मजबूर करने के लिए कुछ भी मजबूर नहीं है। जहाँ तक कंप्यूटर की बात है, बिना IO सनद के, वे पहले दो भाव एक दूसरे से पूरी तरह स्वतंत्र हैं। सौभाग्य से, nameदूसरे दो पर एक आदेश लागू होता है।

आदेश निर्भरता की समस्या को हल करने के अन्य तरीके हैं, लेकिन IO मोनड का उपयोग करना संभवतः सबसे सरल तरीका है (भाषा की दृष्टि से कम से कम) सब कुछ आलसी कार्यात्मक क्षेत्र में रहने की अनुमति देने के लिए, बिना किसी अनिवार्यता के छोटे वर्गों के। यह सबसे लचीला भी है। उदाहरण के लिए, आप अपेक्षाकृत आसानी से उपयोगकर्ता इनपुट के आधार पर गतिशील रूप से एक IO पाइपलाइन का निर्माण कर सकते हैं।


2

कार्यात्मक प्रोग्रामिंग की मेरी (सीमित) समझ यह है कि राज्य / साइड इफेक्ट्स को कम से कम किया जाना चाहिए और स्टेटलेस लॉजिक से अलग रखा जाना चाहिए।

यह सिर्फ कार्यात्मक प्रोग्रामिंग नहीं है; यह आमतौर पर किसी भी भाषा में एक अच्छा विचार है। यदि आप इकाई परीक्षण करते हैं, तो आप जिस तरह से अलग हो जाते हैं read_file(), convert()और write_file()पूरी तरह से स्वाभाविक रूप से आता है, क्योंकि convert()कोड के सबसे जटिल और सबसे बड़े भाग से दूर होने के बावजूद, इसके लिए परीक्षण लिखना अपेक्षाकृत आसान है: आपको बस सेट करने की आवश्यकता है इनपुट पैरामीटर । के लिए परीक्षण लिखना read_file()और write_file()काफी कठिन है (भले ही फ़ंक्शन स्वयं लगभग तुच्छ हैं) क्योंकि आपको फ़ंक्शन को कॉल करने से पहले और बाद में फ़ाइल सिस्टम पर चीजों को बनाने और / या पढ़ने की आवश्यकता है। आदर्श रूप से आप ऐसे कार्यों को इतना सरल बना देंगे कि आप उन्हें परीक्षण न करने में सहज महसूस करें और इस तरह खुद को बहुत परेशानी से बचा सकें।

यहां पायथन और हास्केल के बीच अंतर यह है कि हास्केल के पास एक प्रकार का चेकर है जो यह साबित कर सकता है कि कार्यों का कोई दुष्प्रभाव नहीं है। पायथन में आपको यह उम्मीद करने की ज़रूरत है कि किसी का गलती से फ़ाइल-रीडिंग या-राइटिंग फ़ंक्शन में नहीं गिरा convert()(कहो read_config_file())। हास्केल में जब आप घोषणा करते हैं convert :: String -> Stringया समान होते हैं, बिना किसी IOसनक के साथ , टाइप परीक्षक यह गारंटी देगा कि यह एक शुद्ध फ़ंक्शन है जो केवल इसके इनपुट पैरामीटर पर निर्भर करता है और कुछ नहीं। यदि कोई convertकॉन्फिग फाइल पढ़ने के लिए संशोधित करने का प्रयास करता है तो वे जल्दी से कंपाइलर त्रुटियों को दिखाते हुए देखेंगे कि वे फ़ंक्शन की शुद्धता को तोड़ रहे हैं। (और उम्मीद है कि वे ले जाने के लिए समझदार पर्याप्त होगा read_config_fileसे बाहर convertऔर में उसके परिणाम पारित convert, पवित्रता को बनाए रखने।)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.