तकनीकी रूप से, कंप्यूटर पर आपके द्वारा निष्पादित कोई भी कार्यक्रम अशुद्ध है क्योंकि यह अंततः "इस मूल्य को 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
और इसने कार्यक्रम के शब्दार्थ को नहीं बदला है।
अब, निम्नलिखित कार्यक्रम पर विचार करें।
यहां, हमने 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
फ़ंक्शन शुद्ध है।
function myNumber(n) { this.n = n; }; myNumber.prototype.valueOf = function() { console.log('impure'); return this.n; }; const n = new myNumber(42); add(n, 1);