विशुद्ध रूप से कार्यात्मक भाषाओं के बारे में गलत धारणाएं?


39

मैं अक्सर निम्नलिखित कथनों / तर्कों का सामना करता हूँ:

  1. शुद्ध कार्यात्मक प्रोग्रामिंग भाषाएं साइड इफेक्ट्स की अनुमति नहीं देती हैं (और इसलिए अभ्यास में बहुत कम उपयोग होता है क्योंकि किसी भी उपयोगी कार्यक्रम के साइड इफेक्ट्स होते हैं, जैसे जब यह बाहरी दुनिया के साथ बातचीत करता है)।
  2. शुद्ध कार्यात्मक प्रोग्रामिंग भाषाएं एक प्रोग्राम लिखने की अनुमति नहीं देती हैं जो राज्य को बनाए रखता है (जो प्रोग्रामिंग को बहुत अजीब बनाता है क्योंकि कई एप्लिकेशन में आपको राज्य की आवश्यकता होती है)।

मैं कार्यात्मक भाषाओं का विशेषज्ञ नहीं हूं, लेकिन यहां पर मैंने अब तक इन विषयों के बारे में समझा है।

बिंदु 1 के बारे में, आप पर्यावरण के साथ विशुद्ध रूप से कार्यात्मक भाषाओं में बातचीत कर सकते हैं, लेकिन आपको स्पष्ट रूप से कोड (फ़ंक्शन) को चिह्नित करना होगा जो साइड इफेक्ट्स का परिचय देता है (जैसे हास्केल में मोनैडिक प्रकार के माध्यम से)। साथ ही, जहां तक ​​मुझे पता है कि साइड इफेक्ट्स (विध्वंसक रूप से अपडेट करने वाले डेटा) द्वारा कंप्यूटिंग भी संभव है (भले ही मोनैडिक प्रकारों का उपयोग करना?) भले ही यह काम करने का पसंदीदा तरीका न हो।

बिंदु 2 के बारे में, जहां तक ​​मुझे पता है कि आप कई संगणना चरणों के माध्यम से मूल्यों का प्रसार करके राज्य का प्रतिनिधित्व कर सकते हैं (हास्केल में, फिर से, राक्षसी प्रकारों का उपयोग करके), लेकिन मुझे ऐसा करने का कोई व्यावहारिक अनुभव नहीं है और मेरी समझ अस्पष्ट है।

तो, क्या दो कथन किसी भी अर्थ में सही हैं या वे केवल विशुद्ध रूप से कार्यात्मक भाषाओं के बारे में गलत धारणाएं हैं? अगर वे गलत धारणाएं हैं, तो वे कैसे आए? क्या आप एक (संभवतः छोटा) कोड स्निपेट लिखकर हास्केल मुहावरेदार तरीके को दिखा सकते हैं (1) साइड इफेक्ट्स को लागू करने और (2) राज्य के साथ एक संगणना लागू करने के लिए?


7
मुझे लगता है कि इसमें से अधिकांश उस पर टिका है जिसे आप 'शुद्ध' कार्यात्मक भाषा के रूप में परिभाषित करते हैं।
जे.के.

@ जक: 'शुद्ध' कार्यात्मक भाषाओं को परिभाषित करने की समस्या से बचने के लिए हास्केल अर्थ (जो अच्छी तरह से परिभाषित है) में शुद्धता का अनुमान लगाएं। किन परिस्थितियों में एक कार्यात्मक भाषा को 'शुद्ध' माना जा सकता है, यह भविष्य के प्रश्न का विषय हो सकता है।
जियोर्जियो

दोनों उत्तरों में बहुत सारे स्पष्ट विचार हैं और मुझे यह चुनना मुश्किल था कि कौन सा स्वीकार करना है। मैंने अतिरिक्त pseudo-code उदाहरणों के कारण sepp2k के उत्तर को स्वीकार करने का निर्णय लिया।
जियोर्जियो

जवाबों:


26

इस उत्तर के प्रयोजनों के लिए मैं "विशुद्ध रूप से कार्यात्मक भाषा" को एक कार्यात्मक भाषा के रूप में परिभाषित करता हूं जिसमें फ़ंक्शन संदर्भित रूप से पारदर्शी होते हैं, अर्थात समान तर्कों के साथ एक ही फ़ंक्शन को कई बार कॉल करना हमेशा समान परिणाम देगा। मेरा मानना ​​है कि विशुद्ध रूप से कार्यात्मक भाषा की सामान्य परिभाषा है।

शुद्ध कार्यात्मक प्रोग्रामिंग भाषाएं साइड इफेक्ट्स की अनुमति नहीं देती हैं (और इसलिए अभ्यास में बहुत कम उपयोग होता है क्योंकि किसी भी उपयोगी कार्यक्रम के साइड इफेक्ट्स होते हैं, जैसे जब यह बाहरी दुनिया के साथ बातचीत करता है)।

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

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

बिंदु 1 के बारे में, आप पर्यावरण के साथ विशुद्ध रूप से कार्यात्मक भाषाओं में बातचीत कर सकते हैं लेकिन आपको कोड (फ़ंक्शन) को स्पष्ट रूप से चिह्नित करना होगा जो उन्हें पेश करता है (उदाहरण के लिए हास्केल में मोनैडिक प्रकार के माध्यम से)।

यह थोड़ा सरल है। बस एक ऐसी प्रणाली होने पर जहां साइड-इफ़ेक्टिंग फ़ंक्शंस को इस तरह से चिह्नित किया जाना चाहिए (सी ++ में कॉन्स्ट-करेक्शन के समान, लेकिन सामान्य साइड-इफ़ेक्ट के साथ) संदर्भात्मक पारदर्शिता सुनिश्चित करने के लिए पर्याप्त नहीं है। आपको यह सुनिश्चित करने की आवश्यकता है कि एक प्रोग्राम एक ही तर्कों के साथ कई बार किसी फ़ंक्शन को कॉल नहीं कर सकता है और विभिन्न परिणाम प्राप्त कर सकता है। आप या तो ऐसा कर सकते हैं जैसे चीजें बनाकरreadLineकुछ ऐसा हो जो कोई फ़ंक्शन न हो (जो कि हास्केल आईओ मोनड के साथ करता है) या आप एक ही तर्क के साथ कई बार साइड-इफ़ेक्टिंग फ़ंक्शन को कॉल करना असंभव बना सकते हैं (यह वही है जो क्लीन करता है)। बाद के मामले में कंपाइलर यह सुनिश्चित करेगा कि हर बार जब आप किसी साइड-इफ़ेक्टिंग फंक्शन को कॉल करते हैं, तो आप एक ताज़ा तर्क के साथ ऐसा करते हैं, और यह किसी भी प्रोग्राम को अस्वीकार कर देता है जहाँ आप एक साइड-इफ़ेक्टिंग फंक्शन को दो बार पास करते हैं।

शुद्ध कार्यात्मक प्रोग्रामिंग भाषाएं एक प्रोग्राम लिखने की अनुमति नहीं देती हैं जो राज्य को बनाए रखता है (जो प्रोग्रामिंग को बहुत अजीब बनाता है क्योंकि कई एप्लिकेशन में आपको राज्य की आवश्यकता होती है)।

फिर, एक विशुद्ध रूप से कार्यात्मक भाषा उत्परिवर्ती राज्य को बहुत अच्छी तरह से अस्वीकार कर सकती है, लेकिन यह निश्चित रूप से शुद्ध होना संभव है और अभी भी परिवर्तनशील राज्य है, यदि आप इसे उसी तरह से लागू करते हैं जैसा कि मैंने ऊपर दिए गए साइड-इफेक्ट्स के साथ वर्णित किया है। वास्तव में उत्परिवर्तनीय स्थिति साइड-इफेक्ट का दूसरा रूप है।

उस ने कहा, कार्यात्मक प्रोग्रामिंग भाषा निश्चित रूप से उत्परिवर्तनीय स्थिति - शुद्ध लोगों को विशेष रूप से हतोत्साहित करती हैं। और मुझे नहीं लगता है कि यह प्रोग्रामिंग अजीब बनाता है - काफी विपरीत। कभी-कभी (लेकिन यह सब अक्सर नहीं) उत्परिवर्तनीय स्थिति को प्रदर्शन या स्पष्टता को खोए बिना टाला नहीं जा सकता है (यही वजह है कि हास्केल जैसी भाषाओं में उत्परिवर्तित राज्य के लिए सुविधाएं हैं), लेकिन सबसे अधिक बार यह हो सकता है।

अगर वे गलत धारणाएं हैं, तो वे कैसे आए?

मुझे लगता है कि बहुत से लोग बस "एक फ़ंक्शन को एक ही परिणाम का उत्पादन करना चाहिए जब उसी तर्क के साथ बुलाया जाता है" और इससे निष्कर्ष निकाला जाए कि कुछ ऐसा लागू करना संभव नहीं है जैसे readLineया कोड जो कि उत्परिवर्तनीय स्थिति बनाए रखता है। तो वे बस "धोखा" के बारे में पता नहीं है कि विशुद्ध रूप से कार्यात्मक भाषाओं को संदर्भित पारदर्शिता को तोड़ने के बिना इन चीजों को पेश करने के लिए उपयोग कर सकते हैं।

साथ ही उत्परिवर्तनीय अवस्था कार्यात्मक भाषाओं में भारी रूप से हतोत्साहित करती है, इसलिए यह पूरी तरह से कार्यात्मक लोगों में इसकी अनुमति नहीं है।

क्या आप एक (संभवतः छोटा) कोड स्निपेट लिखकर हास्केल मुहावरेदार तरीके को दिखा सकते हैं (1) साइड इफेक्ट्स को लागू करने और (2) राज्य के साथ एक संगणना लागू करने के लिए?

यहाँ छद्म-हास्केल में एक एप्लिकेशन है जो उपयोगकर्ता से एक नाम पूछता है और उसे बधाई देता है। स्यूडो-हास्केल एक ऐसी भाषा है जिसका मैंने अभी आविष्कार किया है, जिसमें हास्केल की आईओ प्रणाली है, लेकिन अधिक पारंपरिक वाक्यविन्यास, अधिक वर्णनात्मक फ़ंक्शन नामों का उपयोग किया गया है और कोई भी doटिप्पणी नहीं है (जैसा कि सिर्फ इस बात से विचलित होगा कि आईओ मोनड कैसे काम करता है):

greet(name) = print("Hello, " ++ name ++ "!")
main = composeMonad(readLine, greet)

यहाँ सुराग यह है कि readLineएक प्रकार का मान है IO<String>और composeMonadएक फ़ंक्शन है जो प्रकार का तर्क लेता है IO<T>(कुछ प्रकार के लिए T) और दूसरा तर्क एक फ़ंक्शन है जो प्रकार का तर्क लेता है Tऔर प्रकार का मान लौटाता है IO<U>(कुछ प्रकार के लिए U)। printएक फ़ंक्शन है जो एक स्ट्रिंग लेता है और प्रकार का मान लौटाता है IO<void>

प्रकार IO<A>का एक मान एक मान है जो किसी दिए गए कार्य को "एनकोड" करता है जो प्रकार के मूल्य का उत्पादन करता है AcomposeMonad(m, f)एक नए IOमूल्य का उत्पादन करता है, जो की कार्रवाई के mबाद कार्रवाई करता है f(x), जहां xका मूल्य कार्रवाई का उत्पादन करता है m

म्यूटेबल स्थिति इस तरह दिखाई देगी:

counter = mutableVariable(0)
increaseCounter(cnt) =
    setIncreasedValue(oldValue) = setValue(cnt, oldValue + 1)
    composeMonad(getValue(cnt), setIncreasedValue)

printCounter(cnt) = composeMonad( getValue(cnt), print )

main = composeVoidMonad( increaseCounter(counter), printCounter(counter) )

यहां mutableVariableएक फ़ंक्शन है जो किसी भी प्रकार का मूल्य लेता है Tऔर उत्पादन करता है MutableVariable<T>। फ़ंक्शन getValueलेता है MutableVariableऔर रिटर्न करता IO<T>है जो इसके वर्तमान मूल्य का उत्पादन करता है। setValueएक MutableVariable<T>और एक लेता है और एक Tरिटर्न देता है जो IO<void>मान सेट करता है। composeVoidMonadरूप में ही है composeMonad, सिवाय इसके कि पहले तर्क एक है IOकि एक समझदार मूल्य और दूसरा तर्क का उत्पादन नहीं करता एक और इकाई, नहीं एक समारोह है कि रिटर्न एक इकाई है।

हास्केल में कुछ संश्लिष्ट चीनी होती है, जो इस पूरे क्रम को कम दर्दनाक बनाती है, लेकिन यह अभी भी स्पष्ट है कि उत्परिवर्तनीय स्थिति ऐसी चीज है जो भाषा वास्तव में आपको नहीं करना चाहती है।


शानदार जवाब, बहुत सारे विचारों को स्पष्ट करना। क्या कोड स्निपेट की अंतिम पंक्ति नाम का उपयोग करना चाहिए counter, अर्थात increaseCounter(counter)?
जियोर्जियो

@ जियोर्जियो हाँ, यह चाहिए। फिक्स्ड।
sepp2k

1
@ जियोर्जियो एक बात मैं अपनी पोस्ट में स्पष्ट रूप से उल्लेख करना भूल गया कि आईओ द्वारा की गई कार्रवाई mainवास्तव में निष्पादित होने वाली एक होगी। आईओ को लौटाने के अलावा अन्य कार्यों mainको निष्पादित करने का कोई तरीका नहीं है IO( unsafeउनके नाम पर भयानक बुरे कार्यों का उपयोग किए बिना )।
sepp2k

ठीक है। स्कारफ्रिज ने विनाशकारी IOमूल्यों का भी उल्लेख किया । मुझे समझ नहीं आया कि क्या वह पैटर्न मिलान का संदर्भ देता है, अर्थात इस तथ्य से कि आप बीजगणितीय डेटा प्रकार के मूल्यों को फिर से संगठित कर सकते हैं, लेकिन IOमूल्यों के साथ ऐसा करने के लिए कोई भी पैटर्न मिलान का उपयोग नहीं कर सकता है।
जियोर्जियो

16

IMHO आप भ्रमित हैं क्योंकि शुद्ध भाषा और शुद्ध फ़ंक्शन के बीच अंतर है । आइए हम फ़ंक्शन के साथ शुरू करते हैं। एक फ़ंक्शन शुद्ध होता है यदि वह (उसी इनपुट को दिया जाता है) हमेशा समान मान लौटाता है और किसी भी अवलोकन दुष्प्रभाव का कारण नहीं बनता है। विशिष्ट उदाहरण गणितीय कार्य हैं जैसे f (x) = x * x। अब इस फ़ंक्शन के कार्यान्वयन पर विचार करें। यह अधिकांश भाषाओं में शुद्ध होगा, यहां तक ​​कि जिन्हें आमतौर पर शुद्ध कार्यात्मक भाषाओं जैसे एमएल नहीं माना जाता है। यहां तक ​​कि इस व्यवहार के साथ एक जावा या सी ++ विधि को शुद्ध माना जा सकता है।

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

तो क्या होगा अगर आपको कुछ आंतरिक स्थिति की आवश्यकता है? आप केवल इनपुट पैरामीटर के रूप में संगणना से पहले और परिणाम के भाग के रूप में संगणना के बाद राज्य को जोड़कर शुद्ध भाषा में राज्य की नकल कर सकते हैं। इसके बदले Int -> Boolआपको कुछ मिलता है Int -> State -> (Bool, State)। आप केवल निर्भरता को स्पष्ट करते हैं (जिसे किसी भी प्रोग्रामिंग प्रतिमान में अच्छा अभ्यास माना जाता है)। BTW एक मोनाड है जो इस तरह के राज्य-नकल कार्यों को बड़े राज्य-नकल कार्यों में संयोजित करने के लिए एक विशेष रूप से सुरुचिपूर्ण तरीका है। इस तरह आप निश्चित रूप से शुद्ध भाषा में "राज्य बनाए रख सकते हैं"। लेकिन आपको इसे स्पष्ट करना होगा।

तो क्या इसका मतलब मैं बाहर के साथ बातचीत कर सकता हूं? सभी उपयोगी कार्यक्रम के बाद वास्तविक दुनिया के साथ बातचीत करनी चाहिए ताकि उपयोगी हो। लेकिन इनपुट और आउटपुट स्पष्ट रूप से शुद्ध नहीं हैं। किसी विशिष्ट फ़ाइल के लिए एक विशिष्ट बाइट लिखना पहली बार ठीक हो सकता है। लेकिन ठीक उसी ऑपरेशन को अंजाम देने से दूसरी बार कोई त्रुटि हो सकती है क्योंकि डिस्क भरी हुई है। स्पष्ट रूप से कोई शुद्ध भाषा (आदर्शवादी अर्थ में) मौजूद नहीं है जो किसी फ़ाइल को लिख सके।

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

हास्केल में यह IO प्रकार के साथ किया जाता है। आप एक IO परिणाम (असुरक्षित तंत्र के बिना) को नष्ट नहीं कर सकते। इस प्रकार आप केवल IO परिणामों को उन कार्यों के साथ संसाधित कर सकते हैं जो IO मॉड्यूल में स्वयं परिभाषित हैं। सौभाग्य से एक बहुत ही लचीला कॉम्बिनेटर है जो आपको एक IO परिणाम लेने की अनुमति देता है और इसे एक फ़ंक्शन में संसाधित करता है जब तक कि फ़ंक्शन एक और IO परिणाम लौटाता है। इस कॉम्बिनेटर को बाइंड (या >>=) कहा जाता है और इसका प्रकार होता है IO a -> (a -> IO b) -> IO b। यदि आप इस अवधारणा को सामान्य करते हैं तो आप भिक्षु वर्ग में पहुंच जाते हैं और IO इसका एक उदाहरण है।


4
मैं वास्तव में यह नहीं देखता कि हास्केल ( unsafeइसके नाम के साथ किसी भी फ़ंक्शन को अनदेखा करना ) आपकी आदर्शवादी परिभाषा को पूरा नहीं करता है। हास्केल (फिर से अनदेखी unsafePerformIOऔर सह) में कोई अशुद्ध कार्य नहीं हैं ।
sepp2k

4
readFileऔर writeFileहमेशा वही IOमान लौटाएंगे , जो समान तर्क दिए गए हैं। तो जैसे दो कोड स्निपेट let x = writeFile "foo.txt" "bar" in x >> xऔर writeFile "foo.txt" "bar" >> writeFile "foo.txt" "bar"एक ही काम करेंगे।
sepp2k

3
@ AidanCully "IO फ़ंक्शन" से आपका क्या तात्पर्य है? एक फ़ंक्शन जो प्रकार का मान लौटाता है IO Something? यदि हां, तो एक ही तर्क के साथ दो बार IO फ़ंक्शन को कॉल करना पूरी तरह से संभव है: putStrLn "hello" >> putStrLn "hello"- यहाँ दोनों putStrLnको एक ही तर्क के लिए कॉल करना है। बेशक, यह कोई समस्या नहीं है क्योंकि, जैसा कि मैंने पहले कहा था, दोनों कॉल के परिणामस्वरूप एक ही IO मूल्य होगा।
sepp2k

3
@scarfridge का मूल्यांकन writeFile "foo.txt" "bar"करना त्रुटि का कारण नहीं बन सकता क्योंकि फ़ंक्शन कॉल का मूल्यांकन करने से कार्रवाई निष्पादित नहीं होती है। यदि आप कह रहे हैं कि मेरे पिछले उदाहरण में letI के साथ संस्करण में केवल एक ही IO विफलता का कारण है, जबकि संस्करण के बिना letदो हैं, तो आप गलत हैं। दोनों संस्करण में एक IO विफलता के लिए दो अवसर हैं। चूंकि letसंस्करण कॉल का मूल्यांकन writeFileकेवल एक बार करता है जबकि संस्करण बिना letदो बार मूल्यांकन करता है, आप देख सकते हैं कि इससे कोई फर्क नहीं पड़ता कि फ़ंक्शन कितनी बार कहा जाता है। यह केवल मायने रखता है कि परिणाम कितनी बार ...
sepp2k

6
@AidanCully "मोनड तंत्र" निहित मापदंडों के आसपास से नहीं गुजरता है। putStrLnसमारोह ठीक एक तर्क, प्रकार का है जो लेता है String। यदि आप मुझ पर विश्वास नहीं करते हैं, तो इसके प्रकार को देखें String -> IO ():। यह निश्चित रूप से प्रकार का कोई तर्क नहीं लेता है IO- यह उस प्रकार के मूल्य का उत्पादन करता है।
sepp2k
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.