Inovasi yang paling jelas diperhatikan oleh orang-orang yang baru mengenal Haskell adalah bahwa ada pemisahan antara dunia yang tidak murni yang berkaitan dengan komunikasi dengan dunia luar, dan dunia murni dari komputasi dan algoritma. Pertanyaan pemula yang sering muncul adalah "Bagaimana saya bisa menyingkirkan IO
, yaitu, dikonversi IO a
menjadi a
?" Cara untuk itu adalah dengan menggunakan monads (atau abstraksi lainnya) untuk menulis kode yang melakukan efek IO dan rantai. Kode ini mengumpulkan data dari dunia luar, membuat modelnya, melakukan perhitungan, mungkin dengan menggunakan kode murni, dan menampilkan hasilnya.
Sejauh menyangkut model di atas, saya tidak melihat ada yang sangat salah dengan memanipulasi GUI di IO
monad. Masalah terbesar yang muncul dari gaya ini adalah bahwa modul tidak dapat dikomposisikan lagi, yaitu, saya kehilangan sebagian besar pengetahuan saya tentang urutan eksekusi pernyataan di program saya. Untuk memulihkannya, saya harus menerapkan alasan yang sama seperti pada kode GUI imperatif. Sementara itu, untuk kode non-GUI yang tidak murni, urutan eksekusi jelas karena definisi operator IO
monad >==
(setidaknya selama hanya ada satu utas). Untuk kode murni, tidak masalah sama sekali, kecuali dalam kasus sudut untuk meningkatkan kinerja atau untuk menghindari evaluasi yang mengakibatkan ⊥
.
Perbedaan filosofis terbesar antara konsol dan IO grafis adalah bahwa program yang menerapkan biasanya ditulis dalam gaya sinkron. Hal ini dimungkinkan karena ada (mengabaikan sinyal dan deskriptor file terbuka lainnya) hanya satu sumber peristiwa: aliran byte yang biasa disebut stdin
. GUI pada dasarnya asinkron, dan harus bereaksi terhadap kejadian keyboard dan klik mouse.
Filosofi populer melakukan asynchronous IO dengan cara fungsional disebut Functional Reactive Programming (FRP). Ini mendapat banyak daya tarik baru-baru ini dalam bahasa yang tidak murni, non-fungsional berkat perpustakaan seperti ReactiveX , dan kerangka kerja seperti Elm. Singkatnya, ini seperti melihat elemen GUI dan hal-hal lain (seperti file, jam, alarm, keyboard, mouse) sebagai sumber acara, yang disebut "dapat diamati", yang memancarkan aliran peristiwa. Peristiwa ini digabungkan dengan menggunakan operator akrab seperti map
, foldl
, zip
, filter
, concat
, join
, dll, untuk menghasilkan aliran baru. Ini berguna karena status program itu sendiri dapat dilihat sebagai scanl . map reactToEvents $ zipN <eventStreams>
program, di mana N
sama dengan jumlah yang dapat diamati yang pernah dipertimbangkan oleh program.
Bekerja dengan FRP yang dapat diamati memungkinkan untuk memulihkan kompabilitas karena peristiwa dalam aliran diperintahkan dalam waktu. Alasannya adalah bahwa abstraksi aliran peristiwa memungkinkan untuk melihat semua yang dapat diamati sebagai kotak hitam. Pada akhirnya, menggabungkan aliran acara menggunakan operator memberikan kembali beberapa pemesanan lokal pada eksekusi. Ini memaksa saya untuk menjadi lebih jujur tentang invarian mana yang benar-benar diandalkan oleh program saya, mirip dengan cara semua fungsi di Haskell harus transparan transparan: jika saya ingin menarik data dari bagian lain dari program saya, saya harus eksplisit. iklan menyatakan jenis yang sesuai untuk fungsi saya. (The IO monad, menjadi bahasa Domain-Khusus untuk menulis kode tidak murni, secara efektif menghindari ini)