Najbardziej widoczną innowacją zauważoną przez nowych użytkowników Haskell jest oddzielenie nieczystego świata związanego z komunikowaniem się ze światem zewnętrznym od czystego świata obliczeń i algorytmów. Często zadawane pytanie dla początkujących brzmi: „Jak się pozbyć IO
, tzn. Zamienić IO a
na a
?”. Sposób na to polega na użyciu monad (lub innych abstrakcji) do napisania kodu wykonującego efekty IO i łańcuchowe. Ten kod zbiera dane ze świata zewnętrznego, tworzy jego model, wykonuje pewne obliczenia, prawdopodobnie wykorzystując czysty kod i generuje wynik.
Jeśli chodzi o powyższy model, nie widzę nic strasznie złego w manipulowaniu GUI w IO
monadzie. Największym problemem wynikającym z tego stylu jest to, że moduły nie są już możliwe do skomponowania, tzn. Tracę większość mojej wiedzy na temat globalnej kolejności wykonywania instrukcji w moim programie. Aby go odzyskać, muszę zastosować podobne rozumowanie jak w przypadku współbieżnego, bezwzględnego kodu GUI. Tymczasem w przypadku nieczystego kodu bez GUI kolejność wykonywania jest oczywista ze względu na definicję operatora IO
monady >==
(przynajmniej tak długo, jak istnieje tylko jeden wątek). W przypadku czystego kodu nie ma to żadnego znaczenia, z wyjątkiem przypadków narożnych, aby zwiększyć wydajność lub uniknąć wyników oceny ⊥
.
Największą filozoficzną różnicą między konsolowym a graficznym We / Wy jest to, że programy implementujące te pierwsze są zwykle pisane w stylu synchronicznym. Jest to możliwe, ponieważ istnieje (pomijając sygnały i inne otwarte deskryptory plików) tylko jedno źródło zdarzeń: powszechnie nazywany strumień bajtów stdin
. GUI są z natury asynchroniczne i muszą reagować na zdarzenia z klawiatury i kliknięcia myszą.
Popularna filozofia wykonywania asynchronicznych operacji we / wy w sposób funkcjonalny nosi nazwę Functional Reactive Programming (FRP). Ostatnio zyskał dużą przyczepność w nieczystych, niefunkcjonalnych językach dzięki bibliotekom takim jak ReactiveX i frameworkom takim jak Elm. W skrócie, to jak przeglądanie elementów GUI i innych rzeczy (takich jak pliki, zegary, alarmy, klawiatura, mysz) jako źródeł zdarzeń, zwanych „obserwowalnymi”, które emitują strumienie zdarzeń. Zdarzenia te są łączone za pomocą znanych, takich jak operatorzy map
, foldl
, zip
, filter
, concat
, join
, itd., W celu wytworzenia nowych strumieni. Jest to przydatne, ponieważ sam stan programu może być postrzegany jako scanl . map reactToEvents $ zipN <eventStreams>
program, gdzie N
jest równy liczbie obserwowalnych kiedykolwiek rozważanych przez program.
Praca z obserwowalnymi FRP pozwala odzyskać kompozycję, ponieważ zdarzenia w strumieniu są uporządkowane w czasie. Powodem jest to, że abstrakcja strumienia zdarzeń umożliwia wyświetlanie wszystkich obserwowalnych obiektów jako czarnych skrzynek. Ostatecznie połączenie strumieni zdarzeń za pomocą operatorów zwróci niektóre lokalne porządki przy wykonywaniu. Zmusza mnie to do większej szczerości w kwestii niezmienników, na których faktycznie polega mój program, podobnie do tego, w jaki sposób wszystkie funkcje w Haskell muszą być referencyjnie przejrzyste: jeśli chcę pobierać dane z innej części mojego programu, muszę wyrazić się jasno reklama deklaruje odpowiedni typ dla moich funkcji. (Monada IO, będąca językiem specyficznym dla domeny do pisania nieczystego kodu, skutecznie to omija)