Dlatego powiedzmy, że zapytanie bazy danych lub zapisanie pliku nie może być wykonane z czysto funkcjonalnego stylu z definicji. To na przykład jeden z powodów, dla których potrzebujemy monad.
Nikt nie „potrzebuje” monad, to tylko jeden ze sposobów opisywania rzeczy. W rzeczywistości prawdopodobnie nie jest to najlepszy sposób. Niektóre formy typowania efektów , typy wyjątkowości lub system oparty na pełnej logice liniowej wydają się bardziej przekonujące w teorii, ale wszystkie są bardziej radykalnymi odstępstwami od dobrze znanych systemów typów i bardziej skomplikowanymi w wyrażaniu. Monadyczne we / wy znalezione w Haskell jest kompromisem między użytecznością a prostotą, ponieważ zasadniczo modeluje w pełni imperatywne programowanie w sposób, który łatwo współistniał z istniejącym systemem typu ML używanym już w języku.
Pytanie brzmi - dlaczego uważamy wyjście STDOUT za coś nieczystego? Tak, każdy moduł obsługi plików jest ryzykowny - nigdy nie możemy być pewni, że dane zawsze zostaną zapisane. Ale co z STDOUT? Dlaczego powinniśmy myśleć o tym jako o czymś niewiarygodnym? Czy sama ocena jest bardziej zawodna? To znaczy, zawsze możemy pociągnąć za spust, a tym samym przerwać obliczenia.
Nie jest, a my nie. Dane wejściowe i wyjściowe z programu jako całości mogą być po prostu uważane za argumenty i wynikają z traktowania całego programu jako jednej dużej czystej funkcji. Tak długo, jak drukuje to samo na standardowe wyjście, jeśli podajesz to samo ze standardowego wejścia, jest to czysta funkcja. W rzeczywistości, przed wprowadzeniem monadycznego We / Wy, Haskell użył opartego na strumieniu systemu We / Wy, który używał czystych leniwych strumieni do wprowadzania i wysyłania. Porzuciło to, ponieważ najwyraźniej był to ból w użyciu, co może dać ci pojęcie, dlaczego nie słyszałeś o niczym takim. :]
Mówiąc prościej, rozważ minimalistyczny ezoteryczny język, Lazy K :
Lazy K to zbierający śmieci, referencyjnie przejrzysty funkcjonalny język programowania z prostym systemem I / O opartym na strumieniu.
Tym, co odróżnia Lazy K od innych takich języków, jest prawie całkowity brak innych funkcji. Na przykład nie oferuje zintegrowanego systemu typu polimorficznego Hindley-Milner. Nie jest dostarczany z obszerną standardową biblioteką z obsługą niezależnego od platformy programowania GUI i powiązań z innymi językami. Nie można też napisać takiej biblioteki, ponieważ, między innymi, Lazy K nie zapewnia żadnego sposobu definiowania lub odwoływania się do funkcji innych niż wbudowane. Ta niezdolność jest uzupełniana przez brakujące dopasowanie liczb, ciągów znaków lub dowolnego innego typu danych. Niemniej jednak Lazy K jest kompletny w Turingu.
(...)
Leniwe programy K żyją w tej samej ponadczasowej sferze platońskiej, co funkcje matematyczne, co strona Unlambda nazywa „błogosławioną sferą czystego, nietypowego rachunku lambda”. Podobnie jak wyrzucanie elementów bezużytecznych ukrywa proces zarządzania pamięcią przed programistą, tak przejrzystość referencyjna ukrywa proces oceny. Fakt, że pewne obliczenia są konieczne, aby wyświetlić zdjęcie zestawu Mandelbrota lub „uruchomić” program Lazy K, jest szczegółem implementacji. To jest istota programowania funkcjonalnego.
(...)
Jak obsługiwać dane wejściowe i wyjściowe w języku bez skutków ubocznych? W pewnym sensie wkład i wynik nie są skutkami ubocznymi; są to, że tak powiem, efekty przednie i tylne. Tak jest w Lazy K, gdzie program jest po prostu traktowany jako funkcja od przestrzeni możliwych danych wejściowych do przestrzeni możliwych danych wyjściowych.
Wątpię, czy znajdziesz bardziej funkcjonalny język!
Należy jednak pamiętać, że powyższe dotyczy jedynie zasadniczo przyjmowania danych wejściowych i wyjściowych czystej funkcji i łączenia ich ze stdin / stdout „zewnętrznie” w jakiś sposób. Istnieje duża różnica między tym a dostępem do podstawowych operacji we / wy na poziomie systemu. Szczegóły implementacji odczytu i zapisu strumieni mogą wyciekać nieczystości, chyba że zostaną starannie zamknięte.
Sądzę, że jest to główny powód, dla którego nie możesz tego zrobić bezpośrednio w Haskell - rozsądne przypadki użycia są wąskie w porównaniu do monadycznego we / wy, a dla tych drugich jest wiele korzyści z dostępu do prawdziwych rzeczy. Uważam, że dlatego na przykład argumenty wiersza polecenia programu nie są po prostu przekazywane jako argumenty main
, nawet jeśli intuicyjnie wydaje się, że powinny.
Można odzyskać minimalną wersję czegoś takiego w programie konkretnego, choć - podobnie jak uchwycić argumenty czystych wartości, a następnie skorzystać z interact
funkcji do końca programu.