IO - это бесплатная монада?

В сообщениях блога и примерах Марка Симанна я впервые увидел свободных монад как способ структурировать границу между чистым кодом и кодом ввода-вывода. Я понимаю, что свободная монада позволяет вам построить программу (абстрактное синтаксическое дерево - AST) из чистых функций, которые интерпретатор затем преобразует в последовательность вызовов нечистых процедур. Следовательно, этот интерпретатор превращает чистые операции AST в последовательность монадических операций ввода-вывода.

Мне интересно, дублирует ли это то, что среда выполнения Haskell уже делает с монадой ввода-вывода. Если я рассматриваю ввод-вывод как ничего особенного, а как обычную монаду, чья функция связывания >>= упорядочивает состояние «Реального мира» через все монадические операции в вводе-выводе, тогда это упорядочение не обеспечивает никаких вычислений само по себе (как объяснено для свободных монад в отличный ответ здесь). Затем я могу просматривать все действия ввода-вывода, такие как getLine, writeFile и т.п., как операции в свободной монаде ввода-вывода, а среду выполнения Haskell - как интерпретатор. Среда выполнения интерпретирует каждое действие ввода-вывода с помощью некоторого базового системного вызова, вызова C FFI или тому подобного, что явно нечисто.

Итак, в этом представлении функции, возвращающие действия ввода-вывода, просто создают AST, который затем интерпретируется средой выполнения Haskell. Но до этого момента все чисто. С этой точки зрения функция a -> IO b не является нечистой, точно так же, как операция над свободной монадой не является нечистой.

Верна ли эта интуиция? Если нет, то в чем его недостатки?


person Ulrich Schuster    schedule 15.05.2020    source источник
comment
Ключевым отличием здесь является то, что новая программа может быть построена в соответствии со значением, полученным при запуске предыдущей, после, эта программа была интерпретирована и фактически запущена. так что это не похоже на одно объединенное дерево, известное заранее (это будет Аппликатив, а не Монада); он строится по мере того, как мы идем.   -  person Will Ness    schedule 15.05.2020
comment
@WillNess Это верно как для IO, так и для бесплатных монад   -  person Fyodor Soikin    schedule 15.05.2020
comment
просто хотел подчеркнуть этот момент.   -  person Will Ness    schedule 15.05.2020


Ответы (1)


Ваша интуиция верна: функции с типом IO действительно создают дерево действий, которое затем интерпретируется средой выполнения. Что ж, по крайней мере, это правильный взгляд на это (см. Также комментарий Уилла Несса).

Отличие от бесплатной монады в том, что здесь всего один интерпретатор. Вы не можете выбрать другой, и вы не можете реализовать свой собственный, если захотите.

AST свободной монады обладает двумя основными свойствами: во-первых, он композиционен; во-вторых, это поддается анализу. Интерпретатор может анализировать AST, сопоставляя его конструкторы, и соответственно выполнять интерпретацию.

Монада IO разделяет первое из этих свойств, но не второе. Если у вас есть значение IO String, невозможно определить, было ли оно создано с помощью вызова readLn, pure "foo" или чего-то еще.

person Fyodor Soikin    schedule 15.05.2020
comment
Что касается второго свойства, то не потому, что существует только один конструктор (скрытый в глубине реализации), и вещи вроде readLn (если вы думаете об этом как о функции уровня типа с типом Type -> IO Type и pure, просто используйте этот конструктор ? - person chepner; 15.05.2020
comment
@chepner Да, это еще один полу-верный способ взглянуть на это: вы можете сказать, что IO - это бесплатная монада с только одна операция с именем вызвать эту функцию. Я бы сказал, что именно тот факт, что все возможные операции представлены как одна, лишает его второго свойства. - person Fyodor Soikin; 15.05.2020