क्या यह एक शुद्ध कार्य है?


117

अधिकांश स्रोत निम्नलिखित दो गुणों के रूप में एक शुद्ध कार्य को परिभाषित करते हैं:

  1. इसकी वापसी का मूल्य समान तर्कों के लिए समान है।
  2. इसके मूल्यांकन का कोई दुष्प्रभाव नहीं है।

यह पहली शर्त है जो मुझे चिंतित करती है। ज्यादातर मामलों में, न्याय करना आसान है। निम्नलिखित जावास्क्रिप्ट कार्यों पर विचार करें (जैसा कि इस लेख में दिखाया गया है )

शुद्ध:

const add = (x, y) => x + y;

add(2, 4); // 6

अशुद्ध:

let x = 2;

const add = (y) => {
  return x += y;
};

add(4); // x === 6 (the first time)
add(4); // x === 10 (the second time)

यह देखना आसान है कि दूसरा फ़ंक्शन बाद की कॉल के लिए अलग-अलग आउटपुट देगा, जिससे पहली स्थिति का उल्लंघन होगा। और इसलिए, यह अशुद्ध है।

यह हिस्सा मुझे मिलता है।


अब, मेरे प्रश्न के लिए, इस फ़ंक्शन पर विचार करें जो एक दी गई राशि को यूरो में परिवर्तित करता है:

(EDIT - constपहली पंक्ति में उपयोग करना । letपहले अनजाने में उपयोग किया गया ।)

const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x) => {
  return x * exchangeRate;
};

dollarToEuro(100) //90 today

dollarToEuro(100) //something else tomorrow

मान लें कि हम एक db से विनिमय दर प्राप्त करते हैं और यह हर दिन बदलता है।

अब, कोई फर्क नहीं पड़ता कि मैं आज कितनी बार इस फ़ंक्शन को कॉल करता हूं , यह मुझे इनपुट के लिए एक ही आउटपुट देगा 100। हालाँकि, यह मुझे कल एक अलग आउटपुट दे सकता है। मुझे यकीन नहीं है कि यह पहली शर्त का उल्लंघन करता है या नहीं।

IOW, फ़ंक्शन में इनपुट को म्यूट करने के लिए कोई तर्क नहीं है, लेकिन यह एक बाहरी स्थिरांक पर निर्भर करता है जो भविष्य में बदल सकता है। इस मामले में, यह बिल्कुल निश्चित है कि यह दैनिक रूप से बदल जाएगा। अन्य मामलों में, ऐसा हो सकता है; यह नहीं हो सकता है।

क्या हम ऐसे कार्यों को शुद्ध कार्य कह सकते हैं। यदि जवाब नहीं है, तो हम इसे एक होने के लिए कैसे फिर से पा सकते हैं?


6
जेएस जैसी इस तरह की गतिशील भाषा की function myNumber(n) { this.n = n; }; myNumber.prototype.valueOf = function() { console.log('impure'); return this.n; }; const n = new myNumber(42); add(n, 1);
शुद्धता

29
शुद्धता का मतलब है कि आप अपने प्रोग्राम के व्यवहार को बदले बिना फ़ंक्शन कॉल को कोड स्तर पर इसके परिणाम मान से बदल सकते हैं।
बॉब

1
साइड इफेक्ट का गठन करने के बारे में थोड़ा और आगे जाने के लिए, और अधिक सैद्धांतिक शब्दावली के साथ, cs.stackexchange.com/questions/116377/…
गिल्स का SO- बुराई होना बंद हो '

3
आज, समारोह है (x) => {return x * 0.9;}। कल, आपके पास एक अलग फ़ंक्शन होगा जो शुद्ध भी होगा, हो सकता है (x) => {return x * 0.89;}। ध्यान दें कि हर बार जब आप (x) => {return x * exchangeRate;}इसे चलाते हैं तो एक नया फ़ंक्शन बनता है , और यह फ़ंक्शन शुद्ध होता है क्योंकि exchangeRateयह बदल नहीं सकता है।
user253751

2
यह एक अशुद्ध कार्य है, यदि आप इसे शुद्ध बनाना चाहते हैं, तो आप const dollarToEuro = (x, exchangeRate) => { return x * exchangeRate; }; शुद्ध कार्य के लिए उपयोग कर सकते हैं , Its return value is the same for the same arguments.हमेशा 1 सेकंड, 1 दशक तक धारण करना चाहिए। बाद में कोई फर्क नहीं पड़ता
विकाश तिवारी

जवाबों:


133

dollarToEuroकी वापसी मान एक बाहर चर कि कोई तर्क नहीं है पर निर्भर करता है; इसलिए, फ़ंक्शन अशुद्ध है।

उत्तर में कोई नहीं है, तो फिर हम फ़ंक्शन को शुद्ध होने के लिए कैसे रिफैक्ट कर सकते हैं?

एक विकल्प में पास होना है exchangeRate। इस तरह, हर बार तर्क दिया जाता है (something, somethingElse), उत्पादन होने की गारंटी है something * somethingElse:

const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x, exchangeRate) => {
  return x * exchangeRate;
};

ध्यान दें कि कार्यात्मक प्रोग्रामिंग के लिए, आपको बचना चाहिए let- हमेशा constपुन: असाइनमेंट से बचने के लिए उपयोग करें।


6
मुक्त चर के न होने का नहीं है : एक समारोह शुद्ध होने के लिए के लिए एक आवश्यकता const add = x => y => x + y; const one = add(42);यहाँ दोनों addऔर oneशुद्ध कार्य हैं।
बजे

7
const foo = 42; const add42 = x => x + foo;<- यह एक और शुद्ध फ़ंक्शन है, जो फिर से मुफ्त चर का उपयोग करता है।
झटके z

8
@zerkms - मैं इस प्रश्न के आपके उत्तर को देखने के लिए बहुत उत्सुक हूं (भले ही यह निश्चित रूप से विभिन्न शब्दावली का उपयोग करने के लिए है)। मुझे नहीं लगता कि यह डुप्लिकेट होगा, और यह रोशन होगा, खासकर जब उद्धृत (आदर्श रूप से ऊपर विकिपीडिया लेख से बेहतर स्रोतों के साथ, लेकिन अगर यह सब हमें मिलता है, तब भी एक जीत)। (इस टिप्पणी को किसी प्रकार के नकारात्मक प्रकाश में पढ़ना आसान होगा। मुझे विश्वास है कि मैं वास्तविक हो रहा हूं, मुझे लगता है कि इस तरह का उत्तर बहुत अच्छा होगा और इसे पढ़ना चाहेंगे।)
टीजे क्राउडर

17
मुझे लगता है कि आप और @zerkms दोनों गलत हैं। आपको लगता है कि dollarToEuroआपके उत्तर में उदाहरण में फ़ंक्शन अशुद्ध है क्योंकि यह मुक्त चर पर निर्भर करता है exchangeRate। वह बेतुका है। जैसा कि जर्कम ने बताया, एक फ़ंक्शन की शुद्धता का मुफ्त चरों के साथ कोई लेना-देना नहीं है। हालाँकि, झटके भी गलत हैं क्योंकि उनका मानना ​​है कि dollarToEuroफ़ंक्शन अशुद्ध है क्योंकि यह उस पर निर्भर करता है exchangeRateजो डेटाबेस से आता है। वह कहते हैं कि यह अशुद्ध है क्योंकि "यह IO पर निर्भर करता है।"
आदित एम शाह

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

76

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

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

const fib = (() => {
    const memo = [0, 1];

    return n => {
      if (n >= memo.length) memo[n] = fib(n - 1) + fib(n - 2);
      return memo[n];
    };
})();

console.log(fib(100));

हम आंतरिक की परवाह नहीं करते हैं क्योंकि हम शुद्धता के लिए जाँच के लिए एक ब्लैक बॉक्स पद्धति का उपयोग कर रहे हैं। इसी तरह, हमें परवाह नहीं है कि सभी कोड अंततः अशुद्ध मशीन निर्देशों में परिवर्तित हो गए हैं क्योंकि हम एक ब्लैक बॉक्स पद्धति का उपयोग करके शुद्धता के बारे में सोच रहे हैं। आंतरिक महत्वपूर्ण नहीं हैं।

अब, निम्नलिखित फ़ंक्शन पर विचार करें।

const greet = name => {
    console.log("Hello %s!", name);
};

greet("World");
greet("Snowman");

क्या greetकार्य शुद्ध या अशुद्ध है? हमारी ब्लैक बॉक्स कार्यप्रणाली द्वारा, यदि हम इसे एक ही इनपुट देते हैं (उदाहरण के लिए World) तो यह हमेशा उसी आउटपुट को स्क्रीन पर प्रिंट करता है (यानी Hello World!)। इस मायने में, क्या यह शुद्ध नहीं है? नहीं यह नहीं। इसका कारण यह शुद्ध नहीं है क्योंकि हम स्क्रीन पर कुछ छापने को साइड इफेक्ट मानते हैं। यदि हमारा ब्लैक बॉक्स साइड इफेक्ट्स पैदा करता है तो यह शुद्ध नहीं है।

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

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

console.log("Hello %s!", "World");
console.log("Hello %s!", "Snowman");

यहाँ, हमने इसकी परिभाषा को रेखांकित किया है greetऔर इसने कार्यक्रम के शब्दार्थ को नहीं बदला है।

अब, निम्नलिखित कार्यक्रम पर विचार करें।

undefined;
undefined;

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

अब, आइए एक और उदाहरण पर विचार करें। निम्नलिखित कार्यक्रम पर विचार करें।

const main = async () => {
    const response = await fetch("https://time.akamai.com/");
    const serverTime = 1000 * await response.json();
    const timeDiff = time => time - serverTime;
    console.log("%d ms", timeDiff(Date.now()));
};

main();

जाहिर है, mainसमारोह अशुद्ध है। हालांकि, timeDiffफ़ंक्शन शुद्ध या अशुद्ध है? यद्यपि यह इस बात पर निर्भर करता है serverTimeकि यह एक अशुद्ध नेटवर्क कॉल से आता है, यह अभी भी प्रासंगिक रूप से पारदर्शी है क्योंकि यह समान इनपुट के लिए समान आउटपुट देता है और क्योंकि इसका कोई साइड इफेक्ट नहीं है।

zerkms शायद इस बात पर मुझसे असहमत होंगे। अपने जवाब में , उन्होंने कहा कि dollarToEuroनिम्नलिखित उदाहरण में फ़ंक्शन अशुद्ध है क्योंकि "यह IO पर निर्भर करता है।"

const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x, exchangeRate) => {
  return x * exchangeRate;
};

मुझे उससे असहमत होना होगा क्योंकि exchangeRateडेटाबेस से आया तथ्य अप्रासंगिक है। यह एक आंतरिक विवरण है और किसी फ़ंक्शन की शुद्धता का निर्धारण करने के लिए हमारी ब्लैक बॉक्स कार्यप्रणाली आंतरिक विवरणों की परवाह नहीं करती है।

हास्केल जैसी विशुद्ध रूप से कार्यात्मक भाषाओं में, हमारे पास मनमाने ढंग से IO प्रभावों को क्रियान्वित करने के लिए एक एस्केप हैच है। इसे कहा जाता है unsafePerformIO, और जैसा कि नाम का तात्पर्य है यदि आप इसे सही तरीके से उपयोग नहीं करते हैं तो यह सुरक्षित नहीं है क्योंकि यह संदर्भात्मक पारदर्शिता को तोड़ सकता है। हालाँकि, यदि आप जानते हैं कि आप क्या कर रहे हैं तो यह उपयोग करने के लिए पूरी तरह से सुरक्षित है।

यह आमतौर पर प्रोग्राम की शुरुआत के पास कॉन्फ़िगरेशन फ़ाइलों से डेटा लोड करने के लिए उपयोग किया जाता है। कॉन्फ़िगर फ़ाइलों से डेटा लोड करना एक अशुद्ध IO ऑपरेशन है। हालाँकि, हम डेटा को हर फ़ंक्शन के इनपुट के रूप में पारित नहीं करना चाहते हैं। इसलिए, यदि हम उपयोग करते हैं unsafePerformIOतो हम डेटा को शीर्ष स्तर पर लोड कर सकते हैं और हमारे सभी शुद्ध कार्य अपरिवर्तनीय वैश्विक कॉन्फ़िगरेशन डेटा पर निर्भर कर सकते हैं।

ध्यान दें कि सिर्फ इसलिए कि कोई फ़ंक्शन किसी कॉन्फ़िगर फ़ाइल, डेटाबेस या नेटवर्क कॉल से लोड किए गए कुछ डेटा पर निर्भर करता है, इसका मतलब यह नहीं है कि फ़ंक्शन अशुद्ध है।

हालांकि, आइए अपने मूल उदाहरण पर विचार करें जिसमें विभिन्न शब्दार्थ हैं।

let exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x) => {
  return x * exchangeRate;
};

dollarToEuro(100) //90 today

dollarToEuro(100) //something else tomorrow

यहाँ, मैं यह मान रहा हूँ कि क्योंकि exchangeRateइसे परिभाषित नहीं किया गया है const, यह कार्यक्रम के चलने के दौरान संशोधित होने जा रहा है। अगर ऐसा है तो dollarToEuroनिश्चित रूप से एक अशुद्ध कार्य है क्योंकि जब exchangeRateसंशोधित किया जाता है, तो यह संदर्भात्मक पारदर्शिता को तोड़ देगा।

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

ध्यान दें कि exchangeRateहर बार जब आप प्रोग्राम को फिर से चलाते हैं तो मान बदल सकता है और यह रेफ़रेंशियल पारदर्शिता को तोड़ेगा नहीं। यह केवल संदर्भात्मक पारदर्शिता को तोड़ता है अगर यह प्रोग्राम चलने के दौरान बदलता है।

उदाहरण के लिए, यदि आप मेरे timeDiffउदाहरण को कई बार चलाते हैं तो आपको अलग-अलग मान serverTimeऔर इसलिए अलग-अलग परिणाम मिलेंगे । हालाँकि, क्योंकि serverTimeप्रोग्राम चलने के दौरान कभी नहीं बदलता है, तो timeDiffफ़ंक्शन शुद्ध है।


3
यह बहुत जानकारीपूर्ण था। धन्यवाद। और मैं constअपने उदाहरण में उपयोग करने का मतलब था ।
स्नोमैन

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

3
यह ध्वनि सापेक्षता के बारे में एक छोटे सिद्धांत की तरह है: स्थिरांक केवल अपेक्षाकृत स्थिर होते हैं, न कि बिल्कुल, अर्थात् चलने की प्रक्रिया के सापेक्ष। स्पष्ट रूप से यहां एकमात्र सही उत्तर है। +1।
बॉब

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

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

23

मेरे-प्यूरिस्ट का एक उत्तर (जहाँ "मुझे" अक्षरशः मेरे लिए है, क्योंकि मुझे लगता है कि इस प्रश्न का एक भी औपचारिक "सही उत्तर" नहीं है):

बंदर पैच बेस प्रकारों के लिए इतनी सारी संभावनाओं के साथ जेएस के रूप में एक गतिशील भाषा में, या सुविधाओं के उपयोग से कस्टम प्रकार बनाते हैं जैसे कि Object.prototype.valueOfयह बताना असंभव है कि क्या कोई फ़ंक्शन केवल इसे देखकर शुद्ध है, क्योंकि यह कॉलर पर है कि क्या वे चाहते हैं साइड इफेक्ट का उत्पादन करने के लिए।

एक डेमो:

const add = (x, y) => x + y;

function myNumber(n) { this.n = n; };
myNumber.prototype.valueOf = function() {
    console.log('impure'); return this.n;
};

const n = new myNumber(42);

add(n, 1); // this call produces a side effect

मुझे-व्यावहारिक का एक जवाब:

से विकिपीडिया से बहुत परिभाषा

कंप्यूटर प्रोग्रामिंग में, एक शुद्ध कार्य एक फ़ंक्शन होता है जिसमें निम्नलिखित गुण होते हैं:

  1. इसका रिटर्न वैल्यू समान तर्कों के लिए समान है (स्थानीय स्थिर वैरिएबल, नॉन-लोकल वैरिएबल, म्यूटेबल रेफरेंस तर्क या I / O उपकरणों से इनपुट स्ट्रीम के साथ भिन्नता नहीं)।
  2. इसके मूल्यांकन का कोई साइड इफेक्ट नहीं है (स्थानीय स्थैतिक चर, गैर-स्थानीय चर, परस्पर संदर्भ तर्क या I / O स्ट्रीम) का कोई म्यूटेशन नहीं है।

दूसरे शब्दों में, यह केवल यह बताता है कि कोई फ़ंक्शन कैसे व्यवहार करता है, न कि यह कैसे कार्यान्वित किया जाता है। और जब तक एक विशेष फ़ंक्शन इन 2 गुणों को रखता है - यह शुद्ध है चाहे यह बिल्कुल कैसे लागू किया गया हो।

अब आपके समारोह में:

const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x, exchangeRate) => {
  return x * exchangeRate;
};

यह अशुद्ध है क्योंकि यह आवश्यकता 2 को योग्य नहीं करता है: यह IO पर निर्भर करता है।

मैं मानता हूं कि उपरोक्त कथन गलत है, विवरण के लिए अन्य उत्तर देखें: https://stackoverflow.com/a/58749249/251311

अन्य प्रासंगिक संसाधन:


4
@TJCrowder meएक जवाब देने वाले ज़र्कम्स के रूप में।
ज़र्कम्स

2
हाँ, जावास्क्रिप्ट के साथ यह आत्मविश्वास के बारे में है, गारंटी नहीं है
बॉब

4
@bob ... या यह एक अवरुद्ध कॉल है।
ज़र्कम्स

1
@zerkms - धन्यवाद। बस इसलिए मैं 100% सुनिश्चित हूं, आपके add42और मेरे बीच का महत्वपूर्ण अंतर addXपूरी तरह से है कि मेरा xबदला जा सकता है, और आपका ftबदला नहीं जा सकता है (और इस प्रकार, add42वापसी का मूल्य भिन्न नहीं होता है ft)?
टीजे क्राउडर

5
मैं असहमत हूं कि dollarToEuroआपके उदाहरण में समारोह अशुद्ध है। मैंने समझाया कि मैं अपने जवाब में असहमत क्यों हूं। stackoverflow.com/a/58749249/783743
आदित एम शाह

14

जैसे अन्य उत्तरों ने कहा है, जिस तरह से आपने लागू किया है dollarToEuro,

let exchangeRate = fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x) => { return x * exchangeRate; }; 

वास्तव में शुद्ध है, क्योंकि प्रोग्राम चलने के दौरान विनिमय दर अपडेट नहीं होती है। वैचारिक रूप से, हालांकि, dollarToEuroऐसा लगता है कि यह एक अशुद्ध कार्य होना चाहिए, इसमें यह उपयोग करता है कि विनिमय दर सबसे अधिक है। इस विसंगति को समझाने का सबसे सरल तरीका यह है कि आपने लागू नहीं किया है dollarToEuroलेकिनdollarToEuroAtInstantOfProgramStart

यहां कुंजी यह है कि मुद्रा रूपांतरण की गणना करने के लिए कई पैरामीटर हैं, और यह कि सामान्य रूप से शुद्ध संस्करण dollarToEuroउन सभी की आपूर्ति करेगा। सबसे प्रत्यक्ष पैरामीटर कन्वर्ट करने के लिए USD की राशि है, और विनिमय दर। हालाँकि, क्योंकि आप प्रकाशित जानकारी से अपनी विनिमय दर प्राप्त करना चाहते हैं, इसलिए अब आपके पास प्रदान करने के लिए तीन पैरामीटर हैं:

  • विनिमय करने के लिए धन की राशि
  • विनिमय दरों के लिए परामर्श करने के लिए एक ऐतिहासिक प्राधिकरण
  • वह तारीख जिस पर लेन-देन हुआ (ऐतिहासिक प्राधिकरण को सूचीबद्ध करने के लिए)

यहां ऐतिहासिक प्राधिकरण आपका डेटाबेस है, और यह मानते हुए कि डेटाबेस से समझौता नहीं किया गया है, हमेशा एक विशेष दिन पर विनिमय दर के लिए एक ही परिणाम लौटाएगा। इसलिए, इन तीन मापदंडों के संयोजन से, आप सामान्य रूप से पूरी तरह से शुद्ध, आत्मनिर्भर संस्करण लिख सकते हैं dollarToEuro, जो कुछ इस तरह दिख सकता है:

function dollarToEuro(x, authority, date) {
    const exchangeRate = authority(date);
    return x * exchangeRate;
}

dollarToEuro(100, fetchFromDatabase, Date.now());

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

यदि आप चाहते हैं कि इसका एक शुद्ध संस्करण dollarToEuroअभी भी सबसे अद्यतित मूल्य प्राप्त कर सके, तो आप अभी भी ऐतिहासिक प्राधिकरण को बाँध सकते हैं, लेकिन तिथि पैरामीटर को अबाधित छोड़ सकते हैं और कॉल करने वाले से तर्क के रूप में तारीख पूछ सकते हैं। कुछ इस तरह से:

function dollarToEuro(x, date) {
    const exchangeRate = fetchFromDatabase(date);
    return x * exchangeRate;
}

dollarToEuro(100, Date.now());

@Snowman आपका स्वागत है! मैंने अधिक कोड उदाहरण जोड़ने के लिए उत्तर को थोड़ा अपडेट किया।
TheHansinator

8

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

जब आपके पास इस तरह का कोड हो:

let exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;

const dollarToEuro = (x) => {
  return x * exchangeRate;
};

dollarToEuro(100) //90 today

dollarToEuro(100) //something else tomorrow

यदि exchangeRateदोनों कॉल के बीच में कभी भी बदलाव नहीं किया जा सकता है dollarToEuro(100), तो dollarToEuro(100)दूसरी कॉल को ऑप्टिमाइज़ करने और ऑप्टिमाइज़ करने के लिए पहली कॉल के परिणाम को याद रखना संभव है । परिणाम समान होगा, इसलिए हम पहले से मूल्य को याद कर सकते हैं।

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

यदि fetchFromDatabase()अपने आप में एक निरंतर का मूल्यांकन करने वाला एक शुद्ध कार्य है, और exchangeRateअपरिवर्तनीय है, तो हम इस निरंतरता को गणना के माध्यम से सभी तरह से मोड़ सकते हैं। एक संकलक जो यह जानता है कि यह मामला वही कटौती कर सकता है जो आपने टिप्पणी में किया था, जो कि dollarToEuro(100)90.0 का मूल्यांकन करता है, और संपूर्ण अभिव्यक्ति को निरंतर 90.0 से बदल देता है।

हालांकि, अगर fetchFromDatabase()I / O का प्रदर्शन नहीं किया जाता है, जिसे एक साइड-इफेक्ट माना जाता है, तो इसका नाम लिस्ट एस्टनोटिशन के सिद्धांत का उल्लंघन करता है।


8

यह फ़ंक्शन शुद्ध नहीं है, यह एक बाहरी चर पर निर्भर करता है, जो लगभग निश्चित रूप से बदलने वाला है।

इसलिए फ़ंक्शन आपके द्वारा किए गए पहले बिंदु को विफल करता है, यह समान मान नहीं देता है जब समान तर्क के लिए।

इस फ़ंक्शन को "शुद्ध" बनाने के लिए, पास करें exchangeRate एक तर्क के रूप में ।

यह दोनों स्थितियों को पूरा करेगा।

  1. यह हमेशा एक ही मूल्य और विनिमय दर में गुजरते समय एक ही मूल्य लौटाएगा।
  2. इसका कोई साइड इफेक्ट भी नहीं होगा।

उदाहरण कोड:

const dollarToEuro = (x, exchangeRate) => {
  return x * exchangeRate;
};

dollarToEuro(100, fetchFromDatabase())

1
"जो लगभग निश्चित रूप से बदलने जा रहा है" --- यह नहीं है, यह नहीं है const
ज़र्कम्स

7

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

आपके द्वारा दी गई दो संपत्तियां संदर्भात्मक पारदर्शिता के दोनों परिणाम हैं । उदाहरण के लिए, निम्नलिखित फ़ंक्शन f1अशुद्ध है, क्योंकि यह हर बार एक ही परिणाम नहीं देता है (संपत्ति जो आपने 1 नंबर की है):

function f1(x, y) {
  if (Math.random() > 0.5) { return x; }
  return y;
}

हर बार एक ही परिणाम प्राप्त करना महत्वपूर्ण क्यों है? क्योंकि अलग-अलग परिणाम प्राप्त करना फ़ंक्शन कॉल के लिए एक मान से अलग शब्दार्थ का एक तरीका है, और इसलिए संदर्भात्मक पारदर्शिता को तोड़ते हैं।

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

एक और तरीका है कि एक फ़ंक्शन कॉल में अलग-अलग शब्दार्थक हो सकते हैं, बयानों को निष्पादित करके। उदाहरण के लिए:

function f2(x) {
  console.log("foo");
  return x;
}

वापसी का मूल्य f2("bar")हमेशा रहेगा "bar", लेकिन मूल्य के शब्दार्थ "bar"कॉल f2("bar")से अलग हैं क्योंकि उत्तरार्द्ध भी कंसोल में प्रवेश करेगा। एक को दूसरे के साथ बदलने से कार्यक्रम का शब्दार्थ बदल जाएगा, इसलिए यह संदर्भात्मक रूप से पारदर्शी नहीं है, और इसलिए f2यह अशुद्ध है।

क्या आपका dollarToEuroफ़ंक्शन प्रासंगिक रूप से पारदर्शी है (और इसलिए शुद्ध) दो चीजों पर निर्भर करता है:

  • हम जिसे संदर्भात्मक रूप से पारदर्शी मानते हैं, उसका 'दायरा' है
  • क्या exchangeRateकभी उस 'दायरे' में बदल जाएगा

उपयोग करने के लिए कोई "सर्वश्रेष्ठ" गुंजाइश नहीं है; आम तौर पर हम कार्यक्रम के एकल रन, या परियोजना के जीवनकाल के बारे में सोचते हैं। एक सादृश्य के रूप में, कल्पना करें कि हर फ़ंक्शन के रिटर्न वैल्यूज़ कैश हो जाते हैं (जैसे @ aadit-m-shah द्वारा दिए गए उदाहरण में मेमो टेबल): जब हमें कैश क्लियर करने की आवश्यकता होगी, तो यह गारंटी देने के लिए कि बासी मान हमारे लिए हस्तक्षेप नहीं करेंगे अर्थ विज्ञान?

यदि exchangeRateउपयोग कर रहे थे varतो यह प्रत्येक कॉल के बीच बदल सकता है dollarToEuro; हमें प्रत्येक कॉल के बीच किसी भी कैश्ड परिणाम को साफ करने की आवश्यकता होगी, इसलिए बोलने के लिए कोई संदर्भात्मक पारदर्शिता नहीं होगी।

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

यदि हम परियोजना के जीवनकाल को अपने 'दायरे' के रूप में मानते हैं तो हम "सबसे अधिक संदर्भित पारदर्शी" हैं और इसलिए "सबसे शुद्ध", यहां तक ​​कि एक सार अर्थ में भी। हमें अपने काल्पनिक कैश को साफ़ करने की आवश्यकता नहीं होगी। हम डिस्क पर स्रोत कोड को सीधे लिखकर, उनके रिटर्न वैल्यू के साथ कॉल को बदलने के लिए यह "कैशिंग" भी कर सकते हैं। यह भी होगा काम भर में परियोजनाओं, जैसे हम कार्यों की एक ऑनलाइन डेटाबेस और उनकी वापसी मान, जहाँ कोई भी एक समारोह कॉल और देख सकते हैं की कल्पना कर सकते हैं (यदि यह DB में है) के दूसरे पक्ष पर किसी के द्वारा प्रदान की वापसी मान का उपयोग दुनिया जो एक अलग परियोजना पर साल पहले एक समान कार्य करता था।


4

जैसा कि लिखा गया है, यह एक शुद्ध कार्य है। यह कोई साइड इफेक्ट नहीं पैदा करता है। फ़ंक्शन का एक औपचारिक पैरामीटर होता है, लेकिन इसमें दो इनपुट होते हैं, और हमेशा किसी भी दो इनपुट के लिए समान मूल्य का उत्पादन करेंगे।


2

क्या हम ऐसे कार्यों को शुद्ध कार्य कह सकते हैं। यदि जवाब नहीं है, तो हम इसे एक होने के लिए कैसे फिर से पा सकते हैं?

जैसा कि आपने विधिवत उल्लेख किया, "यह मुझे कल एक अलग आउटपुट दे सकता है" । क्या ऐसा होना चाहिए, इसका उत्तर "नहीं" जैसा होगा । यह विशेष रूप से ऐसा है यदि आपके इच्छित व्यवहार की dollarToEuroसही ढंग से व्याख्या की गई है:

const dollarToEuro = (x) => {
  const exchangeRate =  fetchFromDatabase(); // evaluates to say 0.9 for today;
  return x * exchangeRate;
};

हालांकि, एक अलग व्याख्या मौजूद है, जहां इसे शुद्ध माना जाएगा:

const dollarToEuro = ( () => {
    const exchangeRate =  fetchFromDatabase();

    return ( x ) => x * exchangeRate;
} )();

dollarToEuro सीधे ऊपर शुद्ध है।


एक सॉफ्टवेयर इंजीनियरिंग के नजरिए से, यह dollarToEuroफ़ंक्शन पर निर्भरता की घोषणा करने के लिए आवश्यक है fetchFromDatabase। इसलिए, परिभाषा dollarToEuroइस प्रकार है:

const dollarToEuro = ( x, fetchFromDatabase ) => {
  return x * fetchFromDatabase();
};

इस परिणाम के साथ, आधार है कि दिए गए fetchFromDatabaseकार्यों को संतोषजनक ढंग से, तो हम निष्कर्ष निकाल सकते हैं कि के प्रक्षेपण fetchFromDatabaseपर dollarToEuroसंतोषजनक होना चाहिए। या बयान " fetchFromDatabaseका अर्थ है पवित्र होता है" dollarToEuroशुद्ध है (के बाद से fetchFromDatabaseएक है आधार के लिए dollarToEuroकी अदिश पहलू से x

मूल पोस्ट से, मैं समझ सकता हूं कि fetchFromDatabaseयह एक फ़ंक्शन समय है। आइए, उस समझ को पारदर्शी बनाने के लिए रीफैक्टरिंग के प्रयास को बेहतर बनाएं, इसलिए स्पष्ट fetchFromDatabaseरूप से एक शुद्ध कार्य के रूप में योग्य होना चाहिए :

fetchFromDatabase = (टाइमस्टैम्प) => {/ * यहाँ क्रियान्वयन जाता है * /};

अंत में, मैं निम्नानुसार सुविधा को रिफलेक्टर करूंगा:

const fetchFromDatabase = ( timestamp ) => { /* here goes the implementation */ };

// Do a partial application of `fetchFromDatabase` 
const exchangeRate = fetchFromDatabase.bind( null, Date.now() );

const dollarToEuro = ( dollarAmount, exchangeRate ) => dollarAmount * exchangeRate();

नतीजतन, dollarToEuroकेवल यह साबित करके इकाई-परीक्षण किया जा सकता है कि यह सही ढंग से कॉल करता है fetchFromDatabase(या इसके व्युत्पन्न exchangeRate)।


1
यह बहुत रोशन था। +1। धन्यवाद।
स्नोमैन

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

-1

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

जैसा कि दूसरों ने कहा है, हास्केल में, एक परिवर्तनशील चर को पढ़ना आमतौर पर अशुद्ध माना जाता है। चर और परिभाषाओं के बीच एक अंतर है कि चर बाद में बदल सकते हैं, परिभाषाएं हमेशा के लिए समान हैं। तो अगर आपने इसे घोषित किया थाconst (यह मानते हुए कि यह सिर्फ एक है numberऔर कोई परस्पर आंतरिक संरचना नहीं है), तो इससे पढ़ने से एक परिभाषा का उपयोग होगा, जो शुद्ध है। लेकिन आप समय के साथ विनिमय दरों को मॉडल बनाना चाहते थे, और इसके लिए किसी प्रकार की परिवर्तनशीलता की आवश्यकता होती है और फिर आप अशुद्धता में आ जाते हैं।

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

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

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

export class Program<x> {
   // wrapped function value
   constructor(public run: () => Promise<x>) {}
   // promotion of any value into a program which makes that value
   static of<v>(value: v): Program<v> {
     return new Program(() => Promise.resolve(value));
   }
   // applying any pure function to a program which makes its input
   map<y>(fn: (x: x) => y): Program<y> {
     return new Program(() => this.run().then(fn));
   }
   // sequencing two programs together
   chain<y>(after: (x: x) => Program<y>): Program<y> {
    return new Program(() => this.run().then(x => after(x).run()));
   }
}

कुंजी यह है कि यदि आपके पास Program<x>तत्कालीन कोई साइड इफेक्ट नहीं हुआ है और ये पूरी तरह कार्यात्मक हैं। जब तक फ़ंक्शन एक शुद्ध फ़ंक्शन नहीं था तब तक किसी प्रोग्राम पर फ़ंक्शन को मैप करने से कोई साइड इफेक्ट नहीं होता है; दो कार्यक्रमों के अनुक्रमण का कोई दुष्प्रभाव नहीं होता है; आदि।

इसलिए उदाहरण के लिए इसे अपने मामले में लागू करने के लिए, आप कुछ शुद्ध कार्य लिख सकते हैं, जो प्रोग्रामों को आईडी द्वारा उपयोगकर्ताओं को प्राप्त करने के लिए और डेटाबेस को बदलने और JSON डेटा प्राप्त करने के लिए लिखते हैं, जैसे

// assuming a database library in knex, say
function getUserById(id: number): Program<{ id: number, name: string, supervisor_id: number }> {
    return new Program(() => knex.select('*').from('users').where({ id }));
}
function notifyUserById(id: number, message: string): Program<void> {
    return new Program(() => knex('messages').insert({ user_id: id, type: 'notification', message }));
}
function fetchJSON(url: string): Program<any> {
  return new Program(() => fetch(url).then(response => response.json()));
}

और फिर आप URL को कर्ल करने के लिए एक क्रॉन जॉब का वर्णन कर सकते हैं और कुछ कर्मचारियों को देख सकते हैं और अपने पर्यवेक्षक को विशुद्ध रूप से कुशल तरीके से सूचित कर सकते हैं

const action =
  fetchJSON('http://myapi.example.com/employee-of-the-month')
    .chain(eotmInfo => getUserById(eotmInfo.id))
    .chain(employee => 
        getUserById(employee.supervisor_id)
          .chain(supervisor => notifyUserById(
            supervisor.id,
            'Your subordinate ' + employee.name + ' is employee of the month!'
          ))
    );

मुद्दा यह है कि यहां हर एक समारोह पूरी तरह से शुद्ध कार्य है; वास्तव में कुछ भी नहीं हुआ है जब तक कि मैं वास्तव action.run()में इसे गति में सेट नहीं करता। इसके अलावा, मैं इस तरह के कार्य लिख सकता हूं,

// do two things in parallel
function parallel<x, y>(x: Program<x>, y: Program<y>): Program<[x, y]> {
    return new Program(() => Promise.all([x.run(), y.run()]));
}

और यदि जेएस ने वादा रद्द किया था तो हम दो कार्यक्रमों को एक दूसरे से जोड़ सकते थे और पहला परिणाम ले सकते थे और दूसरा रद्द कर सकते थे। (मेरा मतलब है कि हम अभी भी कर सकते हैं, लेकिन यह कम स्पष्ट हो जाता है कि क्या करना है।)

इसी तरह आपके मामले में हम बदलती विनिमय दरों का वर्णन कर सकते हैं

declare const exchangeRate: Program<number>;

function dollarsToEuros(dollars: number): Program<number> {
  return exchangeRate.map(rate => dollars * rate);
}

और exchangeRateएक प्रोग्राम हो सकता है जो एक परिवर्तनशील मूल्य को देखता है,

let privateExchangeRate: number = 0;
export function setExchangeRate(value: number): Program<void> {
  return new Program(() => { privateExchangeRate = value; return Promise.resolve(undefined); });
}
export const exchangeRate: Program<number> = new Program(() => {
  return Promise.resolve(privateExchangeRate); 
});

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

लागत, निश्चित रूप से, यह है कि आपको अंततः .run() कहीं न कहीं कॉल करना होगा , और यह अशुद्ध होगा। लेकिन आपकी गणना की पूरी संरचना को शुद्ध गणना द्वारा वर्णित किया जा सकता है, और आप अपने कोड के मार्जिन की अशुद्धता को धक्का दे सकते हैं।


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

हाँ, मैं सोच रहा था कि लेखक के बगल में इतने सारे डाउनवोट क्यों हैं लेकिन एक भी टिप्पणी नहीं है।
बुध ५
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.