В каком смысле IO Monad чист? - PullRequest
       25

В каком смысле IO Monad чист?

48 голосов
/ 31 октября 2010

У меня была монада IO, описанная мне как монада государства, где государство - это "реальный мир".Сторонники этого подхода к IO утверждают, что это делает операции ввода-вывода чистыми, как в случае ссылочной прозрачности.Это почему?С моей точки зрения кажется, что код внутри монады ввода-вывода имеет множество наблюдаемых побочных эффектов.Кроме того, разве нельзя описать какую-либо непростую функцию, например, функцию реального мира?Например, мы не можем думать о, скажем, malloc C как о функции, которая принимает RealWorld и Int и возвращает указатель и RealWorld , только так, как вIO монада RealWorld подразумевается?

Примечание: я знаю, что такое монада и как она используется.Пожалуйста, не отвечайте со ссылкой на случайное учебное пособие по монаде, если в нем конкретно не указан мой вопрос.

Ответы [ 8 ]

56 голосов
/ 01 ноября 2010

Я думаю, что лучшее объяснение, которое я услышал, было относительно недавно на SO.IO Foo - это рецепт создания Foo.Другой распространенный, более буквальный способ сказать, что это «программа, которая выдает Foo».Это может быть выполнено (много раз), чтобы создать Foo или умереть, пытаясь.Выполнение рецепта / программы - это то, что мы в конечном итоге хотим (иначе зачем писать один?), Но вещь, которая представлена ​​действием IO в нашем коде, это сам рецепт.

Этот рецептчистое значение, в том же точном смысле, что String является чистым значением.Рецепты можно комбинировать и манипулировать интересными, иногда удивительными способами, но многие способы, которыми эти рецепты можно комбинировать (за исключением явно не чистых unsafePerformIO, unsafeCoerce и т. Д.), Все полностью прозрачны, детерминированы,и все такое приятное.Полученный рецепт абсолютно никак не зависит от состояния чего-либо, кроме рецептов, из которых он был построен.

15 голосов
/ 31 октября 2010

Кроме того, разве нельзя описать какую-либо непростую функцию, например, функцию реального мира? Например, разве мы не можем думать о, скажем, malloc C как о функции, которая принимает RealWorld и Int и возвращает указатель и RealWorld, только так же, как в IO-монаде, неявный RealWorld?

Точно ...

Вся идея функционального программирования состоит в том, чтобы описать программы как комбинацию небольших независимых вычислений построения больших вычислений.

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

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

Дело в том, что Haskell всегда чист , IO не меняет этого.

Итак, наши нечистые, независимые коды должны получить общую зависимость - мы должны передать RealWorld. Поэтому, какие бы вычисления с состоянием мы ни хотели выполнить, мы должны передать эту вещь RealWorld, чтобы применить наши изменения - и что бы другие вычисления с состоянием не хотели увидеть или внести изменения, они должны знать также RealWorld.

Неважно, делается ли это явно или неявно через монаду IO. Вы создаете программу на Haskell как гигантские вычисления, которые преобразуют данные, и одна часть этих данных - RealWorld.

Как только начальный main :: IO () вызывается, когда ваша программа запускается с текущим реальным миром в качестве параметра, этот реальный мир переносится через все нечистые вычисления, как и данные в State. Вот о чем заботится монадический >>= (bind).

И там, где RealWorld не получается (как в чистых вычислениях или без >>= -ing до main), нет никаких шансов что-либо сделать с ним. И где он получает , что произошло путем чисто функциональной передачи (неявного) параметра. Вот почему

let foo = putStrLn "AAARGH" in 42

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

10 голосов
/ 01 ноября 2010

Предположим, у нас есть что-то вроде:

animatePowBoomWhenHearNoiseInMicrophone :: TimeDiff -> Sample -> IO ()
animatePowBoomWhenHearNoiseInMicrophone
    levelWeightedAverageHalfLife levelThreshord = ...

programA :: IO ()
programA = animatePowBoomWhenHearNoiseInMicrophone 3 10000

programB :: IO ()
programB = animatePowBoomWhenHearNoiseInMicrophone 3 10000

Вот точка зрения:

animatePowBoomWhenHearNoiseInMicrophone - это чистая функция в том смысле, что ее результаты для одного и того же ввода, programA и programB, абсолютно одинаковы. Вы можете сделать main = programA или main = programB, и это будет точно так же.

animatePowBoomWhenHearNoiseInMicrophone - это функция, получающая два аргумента и приводящая к описанию программы. Среда выполнения Haskell может выполнить это описание, если вы установите для него main или иным образом включите его в main посредством привязки.

Что такое IO? IO - это DSL для описания императивных программ, закодированных в структурах и функциях данных "чистого хаскеля".

"complete-haskell", также известный как GHC, является реализацией как "чистого haskell", так и обязательной реализацией декодера / исполнителя IO.

8 голосов
/ 01 ноября 2010

Это просто сводится к экстенсиональному равенству :

Если бы вам пришлось вызывать getLine дважды, то оба вызова вернули бы IO String, который выглядел бы точно так жеснаружи каждый раз.Если вы напишите функцию, которая будет принимать 2 IO String с и возвращать Bool, чтобы обозначить обнаруженную разницу между ними обоими, было бы невозможно обнаружить какие-либо отличия от каких-либо наблюдаемых свойств.Он не мог спросить любую другую функцию, равны ли они, и любая попытка использования >>= должна также возвращать что-то в IO, что равно внешне .

5 голосов
/ 27 июля 2017

Я позволю Мартину Одерскому ответить на этот вопрос

Монада ввода / вывода не делает функцию чистой. Это просто делает это очевидным что это нечисто.

Звучит достаточно ясно.

3 голосов
/ 31 октября 2010

Несмотря на то, что его название немного странно (в том смысле, что оно не совсем точно соответствует содержимому), следующая ветка haskell-cafe содержит хорошее обсуждение различных моделей ввода-вывода для Haskell.

http://www.mail-archive.com/haskell-cafe@haskell.org/msg79613.html

2 голосов
/ 31 октября 2010

Что ж, это то, чему нас учили в колледже -

Функция прозрачна по ссылкам, когда она всегда возвращает одно и то же значение для указанного ввода (или одно и то же выражение всегда вычисляет одно и то же значение в одном и том же контексте),Поэтому, например, getChar не был бы прозрачным по ссылкам, если бы он имел сигнатуру типа просто () -> Char или Char, потому что вы можете получить разные результаты, если вызываете эту функцию несколько раз с одним и тем же аргументом.

Но, если вы вводите монаду ввода / вывода, то getChar может иметь тип IO Char, и этот тип имеет только одно значение - IO Char.Так что getChar всегда возвращает одно и то же значение, независимо от того, какая клавиша пользователя действительно нажата.

Но вы все еще можете "получить" базовое значение из этой IO Char вещи.Ну, на самом деле не получить, а перейти к другой функции, используя оператор связывания (>>=), чтобы вы могли работать с символом, который пользователь ввел в вашу программу.

1 голос
/ 15 августа 2012

Изобретатель Монад говорит: На нечистом языке такая операция, как тик, будет представлена ​​функцией типа () -> ().Ложный аргумент () необходим для того, чтобы задерживать эффект до тех пор, пока функция не будет применена, и поскольку тип вывода равен (), можно предположить, что цель функции заключается в побочном эффекте.Напротив, здесь тик имеет тип M (): ложный аргумент не требуется, и появление M явно указывает, какой эффект может произойти .

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

...