Teknik olarak, bir bilgisayarda yürüttüğünüz herhangi bir program saf değildir, çünkü sonunda “bu değeri içine taşı eax
” ve “bu değeri içeriğine ekleyin” gibi saf olmayan talimatlara derler eax
. Bu çok yardımcı değil.
Bunun yerine, kara kutuları kullanarak saflığı düşünüyoruz . Bazı kodlar, aynı girdiler verildiğinde her zaman aynı çıktıları üretirse, o zaman saf kabul edilir. Bu tanıma göre, dahili olarak saf olmayan not tablosu kullanmasına rağmen aşağıdaki işlev de saftır.
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));
İçselliği umursamıyoruz çünkü saflığı kontrol etmek için bir kara kutu metodolojisi kullanıyoruz. Benzer şekilde, tüm kodların nihayetinde kirli makine talimatlarına dönüştürülmesine aldırmıyoruz çünkü bir kara kutu metodolojisi kullanarak saflığı düşünüyoruz. İç kısımlar önemli değil.
Şimdi, aşağıdaki işlevi göz önünde bulundurun.
const greet = name => {
console.log("Hello %s!", name);
};
greet("World");
greet("Snowman");
greet
Fonksiyon saf mı yoksa saf mı? Kara kutu metodolojimize göre, aynı girişi verirsek (örn. World
) Her zaman aynı çıkışı ekrana yazdırır (yani Hello World!
). Bu anlamda saf değil mi? Hayır değil. Saf olmasının nedeni, ekrana bir şey yazdırmayı bir yan etki olarak görmemizdir. Kara kutumuz yan etkiler üretiyorsa, o zaman saf değildir.
Yan etki nedir? Referans şeffaflığı kavramının yararlı olduğu yer burasıdır . Bir işlev referans olarak saydamsa, o işlevin uygulamalarını her zaman sonuçlarıyla değiştirebiliriz. Bunun, işlev satır içi ile aynı olmadığını unutmayın .
Satır içi işlevde, programın anlamını değiştirmeden bir işlevin uygulamalarını işlevin gövdesi ile değiştiririz. Bununla birlikte, referans olarak saydam bir işlev, programın anlamını değiştirmeden her zaman dönüş değeri ile değiştirilebilir. Aşağıdaki örneği ele alalım.
console.log("Hello %s!", "World");
console.log("Hello %s!", "Snowman");
Burada, tanımını vurguladık greet
ve programın anlambilimini değiştirmedi.
Şimdi, aşağıdaki programı düşünün.
Burada, greet
işlevin uygulamalarını geri dönüş değerleriyle değiştirdik ve programın anlambilimini değiştirdi. Artık ekrana selam basmıyoruz. Yazdırmanın bir yan etki olarak kabul edilmesinin nedeni budur ve bu nedenle greet
işlev saf değildir. Referans olarak şeffaf değil.
Şimdi başka bir örnek ele alalım. Aşağıdaki programı düşünün.
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();
Açıkçası, main
işlev saf değildir. Ancak, timeDiff
işlev saf mı yoksa saf değil mi? serverTime
Saf olmayan bir ağ çağrısından hangisinin geldiğine bağlı olmasına rağmen , aynı girdiler için aynı çıkışları döndürdüğü ve herhangi bir yan etkisi olmadığı için yine de referans olarak şeffaftır.
zerkms muhtemelen bu noktada benimle aynı fikirde değil. Onun cevabında , dollarToEuro
aşağıdaki örnekteki işlevin saf olmadığını, çünkü “geçişken IO'ya bağlıdır” dedi.
const exchangeRate = fetchFromDatabase(); // evaluates to say 0.9 for today;
const dollarToEuro = (x, exchangeRate) => {
return x * exchangeRate;
};
Onunla aynı fikirde olmam gerekiyor çünkü exchangeRate
bir veritabanından gelenlerin alakasız olması. Bu bir iç detaydır ve bir işlevin saflığını belirlemek için kara kutu metodolojimiz iç detayları önemsemez.
Haskell gibi tamamen işlevsel dillerde, keyfi G / Ç efektleri uygulamak için bir kaçış kapağı var. Deniyor unsafePerformIO
ve doğru şekilde kullanmak istemiyorsanız adından da anlaşılacağı gibi bu referans şeffaflık bozabilir çünkü o zaman güvenli değil. Ancak, ne yaptığınızı biliyorsanız, o zaman kullanmak tamamen güvenlidir.
Genellikle programın başlangıcına yakın yapılandırma dosyalarından veri yüklemek için kullanılır. Yapılandırma dosyalarından veri yüklemek, saf olmayan bir IO işlemidir. Ancak, verileri her fonksiyona girdi olarak iletmekle yükümlü olmak istemiyoruz. Dolayısıyla, eğer kullanırsak unsafePerformIO
, verileri en üst seviyede yükleyebiliriz ve tüm saf fonksiyonlarımız değişmez global yapılandırma verilerine bağlı olabilir.
Bir fonksiyonun bir yapılandırma dosyasından, veritabanından veya ağ çağrısından yüklenen bazı verilere bağlı olması, işlevin saf olmadığı anlamına gelmediğini unutmayın.
Bununla birlikte, farklı anlambilime sahip orijinal örneğinizi düşünelim.
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
Burada, exchangeRate
olarak tanımlanmadığı için const
, program çalışırken değiştirileceğini varsayıyorum . Eğer durum buysa dollarToEuro
, kesinlikle saf olmayan bir işlevdir çünkü exchangeRate
değiştirildiği zaman referans şeffaflığını kıracaktır.
Ancak, exchangeRate
değişken değiştirilmezse ve gelecekte hiçbir zaman değiştirilmezse (yani, sabit bir değerse), o zaman tanımlanmış olmasına rağmen let
, referans şeffaflığını bozmaz. Bu durumda, dollarToEuro
gerçekten saf bir işlevdir.
exchangeRate
Programı her çalıştırdığınızda ve değerinin değişebileceğini ve referans şeffaflığını bozmayacağını unutmayın. Referans şeffaflığını yalnızca program çalışırken değişirse keser.
Örneğin, timeDiff
örneğimi birden çok kez çalıştırırsanız, farklı değerler serverTime
ve dolayısıyla farklı sonuçlar elde edersiniz . Ancak, serverTime
program çalışırken asla değeri değişmediği için timeDiff
işlev saftır.
function myNumber(n) { this.n = n; }; myNumber.prototype.valueOf = function() { console.log('impure'); return this.n; }; const n = new myNumber(42); add(n, 1);