Почему в Haskell нет преобразователя ввода-вывода?

Каждая другая монада поставляется с версией преобразователя, и, насколько я знаю, идея преобразователя является общим расширением монад. Следуя тому, как строятся другие трансформаторы, IOT будет что-то вроде

newtype IOT m a = IOT { runIOT :: m (IO a) }

для которых я мог бы создавать полезные приложения на месте: IOT Maybe может либо выполнять действие ввода-вывода, либо ничего не делать, IOT [] может создавать список, который впоследствии может быть sequenced.

Так почему же в Haskell нет преобразователя ввода-вывода?

(Примечания: я видел этот пост в Haskell Cafe, но не могу понять этого. Кроме того, на странице" Взлом "преобразователя ST упоминается, возможно, связанная проблема в своем описании, но не предлагает никаких подробностей.)


person David    schedule 24.10.2012    source источник
comment
По той же причине нет runIO функции (конечно, без учета unsafePerformIO) ...   -  person stephen tetley    schedule 24.10.2012
comment
1. это ничего не объясняет, 2. в интерфейсе монады нет функции m a -> a, поэтому я не понимаю, как это вообще связано. (Внутреннее устройство bind может быть сколь угодно небезопасным, если интерфейс чистый.)   -  person David    schedule 24.10.2012
comment
Да, это было немного гномично. Нет runIO, потому что вы не можете запустить побочный код, чтобы получить чистый ответ. Точно так же нет оправдания для IOT, потому что нет монады, к которой вы могли бы разумно добавить эффекты IO. В стеке монад ввод-вывод должен быть самой внутренней монадой - вы можете добавлять к нему другие монадические эффекты, но не наоборот.   -  person stephen tetley    schedule 25.10.2012
comment
Вы имеете в виду, что не существует IOT без (разумной) внутренней развёртки каждый раз, когда вы используете связывание, что приводит к непредсказуемому поведению? (Если да, возможно, дайте полный ответ)   -  person David    schedule 25.10.2012
comment
Что нужно runIOT (launchMissiles >> lift []) оценивать?   -  person is7s    schedule 25.10.2012
comment
Если бы я мог проголосовать за этот вопрос более одного раза, я бы это сделал. Подумайте, как можно было бы дать альтернативную семантику IO, как можно было бы интерпретировать ее исключительно в терминах типа данных. Что заставляет кого-то думать, что код ввода-вывода имеет побочные эффекты? Только одна конкретная интерпретация времени выполнения.   -  person pigworker    schedule 25.10.2012
comment
@pigworker - я думаю, что большинство людей считают, что ввод-вывод имеет побочные эффекты, потому что в отчете Haskell указано, что функции побочного действия происходят в монаде ввода-вывода. Помимо этого, вы можете посмотреть comonad.com/reader / 2011 / free-monads-for-less-3   -  person John L    schedule 25.10.2012
comment
@JohnL Да, я имел в виду именно такое лечение. Возможно, в монаде ввода-вывода возникают побочные функции, но это не означает, что все мыслимые семантики для ввода-вывода имеют побочные эффекты. Теперь решение оставить ввод-вывод абстрактным влечет за собой то, что только система времени выполнения может предоставить семантику для ввода-вывода: программисты застряли на этом. Но на самом деле мы можем представить себе подделку монады ввода-вывода, предлагающую тот же интерфейс интерпретируемым образом, с вариантом преобразователя. Мы также можем представить себе альтернативные среды выполнения, которые выполняют ввод-вывод, генерируя значения в подделочном типе ввода-вывода.   -  person pigworker    schedule 25.10.2012
comment
Мой ответ на вопрос на странице stackoverflow.com/a/11794441/946226 также объясняет, почему IOT невозможно.   -  person Joachim Breitner    schedule 25.10.2012


Ответы (1)


Рассмотрим конкретный пример IOT Maybe. Как бы вы написали для этого Monad экземпляр? Начать можно примерно так:

instance Monad (IOT Maybe) where
    return x = IOT (Just (return x))
    IOT Nothing >>= _ = IOT Nothing
    IOT (Just m) >>= k = IOT $ error "what now?"
      where m' = liftM (runIOT . k) m

Теперь у вас есть m' :: IO (Maybe (IO b)), но вам нужно что-то типа Maybe (IO b), где - что наиболее важно - выбор между Just и Nothing должен определяться m'. Как это будет реализовано?

Ответ, конечно же, такой, что нет, потому что не может. Вы также не можете оправдать наличие там unsafePerformIO, скрытого за чистым интерфейсом, потому что по сути вы просите чистое значение - выбор Maybe конструктора - чтобы зависеть от результата чего-то в IO. Nnnnnope, этого не произойдет.

В общем случае ситуация еще хуже, потому что произвольное (универсально определенное) Monad развернуть даже труднее, чем IO.


Между прочим, упомянутый вами преобразователь ST реализован иначе, чем предложенный вами IOT. Он использует внутреннюю реализацию ST как State-подобную монаду с использованием специальных примитивов magic pixie dust, предоставляемых компилятором, и на основе этого определяет StateT-подобный преобразователь. IO реализован внутри как еще более волшебный ST, поэтому гипотетический IOT может быть определен аналогичным образом.

Не то чтобы это действительно что-то меняло, кроме, возможно, лучшего контроля над относительным порядком нечистых побочных эффектов, вызванных IOT.

person C. A. McCann    schedule 24.10.2012
comment
Хороший ответ! Относительно последнего предложения: означает ли это, что трансформеры не универсальны, даже если мы отбросим причудливые RealWorld? Это хорошее совпадение, что существуют (как есть) преобразователи всех других монад, которые мы обычно используем? - person David; 25.10.2012
comment
@ Дэвид: Зависит от того, как ты на это смотришь. Если IO действительно была State монадой, значением состояния которой была вся внешняя вселенная, тогда IOT, определенный как таковой, работал бы правильно, где правильно означает, что Nothing в IOT Maybe отбросит вселенную и, таким образом, положит конец всему существованию. Лично я бы предпочел нынешнюю ситуацию ... - person C. A. McCann; 25.10.2012
comment
Это ошибка того, что значит быть вычислением ввода-вывода для одной конкретной реализации того, как запускать один. - person pigworker; 25.10.2012
comment
А что насчет IOT (Just m) >>= k = IOT $ Just $ m >>= maybe (fail "...") id . runIOT . k? Это плохо, потому что (>>=) может fail (а не должно)? - person JJJ; 26.10.2012
comment
@ht .: Это не соответствует законам монад. - person C. A. McCann; 26.10.2012
comment
Су ... Настоящая корень проблемы в том, что мы не можем сотворить a из Nothing :: Maybe a, которое мы могли бы return, а затем join, верно? Очевидно, тогда это сработало бы для Identity, но есть ли даже другие монады, для которых это могло бы работать? - person Sebastian Graf; 01.09.2016