Подтвердить что ты не робот

В чем смысл чистой чистоты МО?

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Конечно...

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

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

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

Точка - Haskell всегда чиста, IO не меняет этого.

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

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

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

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

let foo = putStrLn "AAARGH" in 42

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

Ответ 3

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

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 для описания императивных программ, закодированных в структурах и функциях "pure-haskell".

"complete-haskell" aka GHC - это реализация как "чистого-haskell", так и императивная реализация декодера IO.

Ответ 4

Это просто сводится к extensional равенству:

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

Ответ 5

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

http://www.mail-archive.com/[email protected]/msg79613.html

Ответ 6

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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