Я не думаю, что вы собираетесь «увидеть тот же пример, предполагающий нечистые функции и то, как нечистота нарушила композиционную способность». Любая ситуация, когда побочные эффекты являются проблемой для способностей, - это та, которая не будет возникать с чистыми функциями.
Но вот пример того, что означает, что люди, когда они говорят «не чистые функции сломать компонуемости»:
Допустим, у вас есть система POS, что-то вроде этого (изобразите это C++ или что-то):
class Sale {
private:
double sub_total;
double tax;
double total;
string state; // "OK", "TX", "AZ"
public:
void calculateSalesTax() {
if (state == string("OK")) {
tax = sub_total * 0.07;
} else if (state == string("AZ")) {
tax = sub_total * 0.056;
} else if (state == string("TX")) {
tax = sub_total * 0.0625;
} // etc.
total = sub_total + tax;
}
void printReceipt() {
calculateSalesTax(); // Make sure total is correct
// Stuff
cout << "Sub-total: " << sub_total << endl;
cout << "Tax: " << tax << endl;
cout << "Total: " << total << endl;
}
Теперь вам нужно добавить поддержку для Oregon (без налога с продаж). Просто добавьте блок:
else if (state == string("OR")) {
tax = 0;
}
к calculateSalesTax
. Но предположим, что кто-то решает получить «умный» и сказать
else if (state == string("OR")) {
return; // Nothing to do!
}
вместо этого. Теперь total
больше не рассчитывается! Поскольку выходы функции calculateSalesTax
не совсем понятны, программист произвел изменение, которое не дает всех правильных значений.
Возврат к Haskell, с чистыми функциями, вышеуказанный дизайн просто не работает; вместо этого, вы должны сказать что-то вроде
calculateSalesTax :: String -> Double -> (Double, Double) -- (sales tax, total)
calculateSalesTax state sub_total = (tax, sub_total + tax) where
tax
| state == "OK" = sub_total * 0.07
| state == "AZ" = sub_total * 0.056
| state == "TX" = sub_total * 0.0625
-- etc.
printReceipt state sub_total = do
let (tax, total) = calculateSalesTax state sub_total
-- Do stuff
putStrLn $ "Sub-total: " ++ show sub_total
putStrLn $ "Tax: " ++ show tax
putStrLn $ "Total: " ++ show total
Теперь очевидно, что штат Орегон должен быть добавлен, добавив его
| state == "OR" = 0
для расчета tax
. Ошибка предотвращается, так как входы и выходы в функцию все явные.
Вот один из моих любимых сообщений о том, как чистота и лень позволяют создавать композицию: http://apfelmus.nfshost.com/articles/quicksearch.html – luqui