STDOUT i jego zanieczyszczenie

10

Przeczytałem wiele książek i artykułów na temat programowania funkcjonalnego i wciąż wstydzę się, że nie jestem w stanie zrozumieć na pewno niektórych bardzo podstawowych pojęć.

Jedną z głównych idei programowania funkcjonalnego jest to, że to samo wejście zawsze powinno generować takie samo wyjście. 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.

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.

shabunc
źródło

Odpowiedzi:

6

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 interactfunkcji do końca programu.

CA McCann
źródło
Sir, muszę wyznać, że podoba mi się twoja odpowiedź na jednym ze stosów. Zdecydowanie powinieneś napisać książkę Haskell, a ja NIE żartuję.
shabunc
@shabunc: Czasami zastanawiałem się, jak blisko suma moich odpowiedzi na SO jest już wielkości książki ...
CA McCann
Czy możesz podać przykład systemu opartego na pełnej logice liniowej? To wydaje się interesujące, jeśli istnieje.
konfigurator
@configurator: Zauważ, jak łączyłem się z określonymi językami dla innych, ale stroną wikipedii dla logiki liniowej? Niestety, gdybym miał przykład, dałbym go. : [Słyszałem tylko o częściowych prototypach i systemach eksperymentalnych z badań CS. Jeśli chcesz zagłębić się w to, oto kilka stosunkowo przystępnych materiałów na temat systemów liniowych, które mogą pomóc Ci zacząć.
CA McCann
3

Chociaż czystość w programie funkcjonalnym jest godnym celem, w rzeczywistości każdy nietrywialny, użyteczny program będzie miał nieczystości (lub „skutki uboczne”) z powodów, o których wspomniałeś.

Całkowicie czyste programy są z definicji zapieczętowaną czarną skrzynką i są zasadniczo nieciekawe.

Język funkcjonalny Haskell rozwiązuje ten problem, izolując skutki uboczne, takie jak dane wyjściowe w monadach . Monada zachowuje czysto funkcjonalny styl programowania, jednocześnie umożliwiając tworzenie wyników.

Robert Harvey
źródło
pewnie masz rację. Ale rozumiem, dlaczego 100% czystości to utopia. Pytanie dotyczy tylko STDOUT.
shabunc
1
STDOUT to efekt uboczny, podobnie jak każdy inny. Wewnętrznie monada przeprowadzałaby wszelkie sprawdzanie błędów, które może być wymagane.
Robert Harvey
tak, to jest właśnie to pytanie - dlaczego uważa się go za efekt uboczny, tak jak każdy inny?
shabunc
2
Wszystko, co modyfikuje świat zewnętrzny, jest uważane za efekt uboczny.
Robert Harvey
1

Zapis do STDOUT może się nie powieść, jeśli nie jesteś podłączony do urządzenia końcowego lub jeśli (z jakiegoś powodu) zamknąłeś dla niego deskryptor pliku.

Ponadto STDOUT nie zawsze jest „ekranem konsoli”. Czasami jest przesyłany do innego programu. Czasami rura jest zepsuta.

Yam Marcovic
źródło
0

Pomaga, jeśli myślisz o czystości w kategoriach „Zmienia stan świata zewnętrznego”. Może to obejmować zapis do konsoli, plik dziennika, wysunięcie płyty CD lub „Uruchamianie pocisków”.

Może to również stanowić problem w kontekście jednoczesnego wykonywania. Jeśli wiesz, że funkcja nie ma skutków ubocznych, możesz łatwo zorganizować współbieżność, ponieważ możesz udowodnić, że nie ma żadnych warunków wyścigu itp.

Zachary K.
źródło
Zmienia stan świata zewnętrznego lub zależy od jego stanu. Zobacz to pytanie, aby uzyskać więcej dyskusji na ten temat.
MatrixFrog,