Це запропоноване «тлумачення» IO
монади. Якщо ви хочете сприймати цю "інтерпретацію" серйозно, тоді вам потрібно сприймати "RealWorld" серйозно. Незалежно від того, action world
отримано спекулятивну оцінку чи ні, action
не має жодних побічних ефектів, її ефекти, якщо такі є, обробляються шляхом повернення нового стану Всесвіту там, де ці ефекти виникли, наприклад, надісланий мережевий пакет. Однак результатом функції є ((),world)
і тому новий стан Всесвіту є world
. Ми не використовуємо новий всесвіт, який, можливо, ми спекулятивно оцінили на стороні. Стан Всесвіту є world
.
Напевно, вам важко сприймати це серйозно. Є багато способів, як це в кращому випадку поверхово парадоксально і безглуздо. Конкурс є особливо неочевидним або божевільним у цій перспективі.
«Почекай, почекай», - кажеш ти. " RealWorld
це просто" маркер ". Це насправді не стан усього Всесвіту". Гаразд, тоді ця "інтерпретація" нічого не пояснює. Тим не менш, як деталь реалізації , це, як моделі GHC IO
. 1 Це означає, що у нас є магічні "функції", які насправді мають побічні ефекти, і ця модель не дає ніяких вказівок щодо їх значення. І оскільки ці функції насправді мають побічні ефекти, ви викликаєте занепокоєння повністю. GHC дійсно повинен вийти зі свого шляху , щоб переконатися , що RealWorld
і ці спеціальні функції не оптимізовані таким чином , щоб змінити цільове поведінка програми.
Особисто (як це, мабуть, очевидно зараз), я вважаю, що ця "миротворча" модель IO
просто марна і заплутана як педагогічний інструмент. (Чи корисно це для впровадження, я не знаю. Для GHC, я думаю, це більше історичний артефакт.)
Один з альтернативних підходів - розглядати IO
як опис запитів з обробниками відповідей. Існує кілька способів зробити це. Напевно, найбільш доступним є використання безкоштовної конструкції монади, зокрема ми можемо використовувати:
data IO a = Return a | Request OSRequest (OSResponse -> IO a)
Існує багато способів зробити це більш досконалим і мати дещо кращі властивості, але це вже вдосконалення. Для розуміння цього не потрібно глибоких філософських припущень щодо природи реальності. Все, що він стверджує, - IO
це або тривіальна програма, Return
яка не робить нічого, крім повернення значення, або це запит в операційну систему з обробником для відповіді. OSRequest
може бути щось на кшталт:
data OSRequest = OpenFile FilePath | PutStr String | ...
Так само OSResponse
може бути щось на кшталт:
data OSResponse = Errno Int | OpenSucceeded Handle | ...
(Одне з удосконалень , які можна зробити, щоб зробити речі більш тіпобезопасно так , що ви знаєте , що ви не отримаєте OpenSucceeded
від PutStr
запиту.) Ці моделі , IO
як описують запитів , які інтерпретуються деякої система (для «реального» IO
монади це сам час виконання Haskell), а потім, можливо, ця система викличе обробник, який ми надали у відповідь. Це, звичайно, також не вказує на те, як PutStr "hello world"
слід обробляти такий запит , але він також не претендує. Зрозуміло, що це делеговано іншій системі. Ця модель також досить точна. Усі користувацькі програми в сучасних ОС повинні робити запити в ОС, щоб зробити що-небудь.
Ця модель забезпечує правильні інтуїції. Наприклад, багато початківців розглядають такі речі, як <-
оператор, як "розгортання" IO
або мають (на жаль, підкріплені) погляди, що IO String
, скажімо, є "контейнером", який містить " String
s" (а потім <-
витягує їх). Цей погляд-відповідь робить цю перспективу явно неправильною. Немає файлової обробки всередині OpenFile "foo" (\r -> ...)
. Поширена аналогія, яка наголошує на цьому, полягає в тому, що в рецепті торта немає торта (або, можливо, «фактура» була б кращою в цьому випадку).
Ця модель також легко працює з одночасністю. Ми можемо легко створити конструктор для OSRequest
подібних, Fork :: (OSResponse -> IO ()) -> OSRequest
і тоді час виконання може переплутати запити, які виробляє цей додатковий обробник, із звичайним обробником, як це не подобається. З деякою кмітливістю ви можете використовувати цю техніку (або пов'язані з нею методи), щоб насправді моделювати речі, такі як паралельність, безпосередньо, а не просто говорити "ми робимо запит в ОС і все відбувається" Так працює IOSpec
бібліотека .
1 Hugs використовував реалізацію на основі продовження, IO
яка приблизно схожа на те, що я описую, хоча і з непрозорими функціями замість явного типу даних. HBC також використовувала реалізацію, що базується на продовженні, шарується над старим IO на основі запиту-відповіді. NHC (і, таким чином, YHC) використовував громовідвід, тобто приблизно, IO a = () -> a
хоча це ()
називалося World
, але це не робить державний прохід. JHC та UHC використовували в основному той самий підхід, що і GHC.