Почему в Haskell нет монады I (только для ввода, в отличие от монады IO)? - PullRequest
21 голосов
/ 17 февраля 2011

Концептуально кажется, что вычисление, которое выполняет вывод, очень отличается от того, которое выполняет только ввод.Последнее, в некотором смысле, намного чище.

Я, например, хотел бы иметь способ отделить только части ввода моей программы от тех, которые могли бы что-то записать.

Итак, почему нет входных данных только для монады?

Есть ли какая-либо причина, почему не получится иметь монаду I (и монаду O, которую можно объединить в монаду IO)?

Редактировать : Я в основном имел ввиду ввод данных как чтение файлов, а не взаимодействие с пользователем.Это также мой вариант использования, где я могу предположить, что входные файлы не меняются во время выполнения программы (в противном случае можно получить неопределенное поведение).

Ответы [ 5 ]

21 голосов
/ 17 февраля 2011

Я не согласен с ответом bdonlan.Это правда, что ни вход, ни выход не являются более «чистыми», но они являются совершенно разными.Вполне справедливо критиковать IO как «мусорное ведро», где все эффекты объединены, и это усложняет обеспечение определенных свойств.Например, если у вас есть много функций, которые, как вы знаете, читают только из определенных областей памяти и которые никогда не могут привести к изменению этих расположений, было бы неплохо знать, что вы можете изменить порядок их выполнения.Или, если у вас есть программа, использующая forkIO и MVars, было бы неплохо узнать, основываясь на ее типе, что она также не читает /etc/passwd.

Кроме того, одна может сочетают монадические эффекты в моде помимо просто сложенных трансформаторов.Вы не можете сделать это с всеми монадами (просто бесплатными монадами), но для такого случая это все, что вам действительно нужно.Например, пакет iospec обеспечивает чистую спецификацию IO - он не разделяет чтение и запись, но отделяет их, например, от STM, MVars, forkIO, soforth.

http://hackage.haskell.org/package/IOSpec

Ключевые идеи, как правильно комбинировать различные монады, описаны в документе Типы данных a la Carte (отличное чтение, очень влиятельный, недостаточно рекомендовать и т. Д..).

12 голосов
/ 17 февраля 2011

Сторона «ввода» монады ввода-вывода - это столько же, сколько и вход.Если вы используете строку ввода, факт того, что вы использовали этот вход , передается извне, а также служит для записи в качестве нечистого состояния (т. Е. Вы больше не потребляете ту же строку позже);это такая же операция вывода, как и putStrLn.Кроме того, операции ввода должны быть упорядочены относительно операций вывода;это опять-таки ограничивает, насколько вы можете разделить их.

Если вы хотите получить чистую монаду только для чтения, вам, вероятно, следует использовать вместо нее монаду reader .

При этомВы, кажется, немного смущены тем, что может сделать объединение монад.В то время как вы действительно можете объединить две монады (при условии, что одна является преобразователем монад) и получить некоторую гибридную семантику, вы должны иметь возможность запустить результат .То есть, даже если бы вы могли определить IT (OT Identity) r, как вы его запустите?В этом случае у вас нет корневой монады IO, поэтому main должна быть чистой функцией.Что означало бы, что у вас будет main = runIdentity . runOT . runIT $ ....Что бессмысленно, поскольку вы получаете нечистые эффекты из чистого контекста.

Другими словами, тип монады IO должен быть фиксированным.Это не может быть выбранный пользователем преобразованный тип, потому что его тип прибит в main.Конечно, вы можете позвонить это I (O Identity), но вы ничего не получите;O (I Identity) будет бесполезным типом, как I [] или O Maybe, потому что вы никогда не сможете запустить ни один из них.

Конечно, если IO оставить какосновной IO тип монады, вы можете определить такие подпрограммы, как:

runI :: I Identity r -> IO r

Это работает, но опять же, вы не можете иметь ничего под этим, я очень легко монаду, и вы не получаете много от этогосложность.В любом случае, что бы означало , если преобразовать монаду Output в базовую монаду List, в любом случае?

3 голосов
/ 17 февраля 2011

Когда вы получаете ввод, вы вызываете побочные эффекты, которые изменяют как состояние внешнего мира (вход используется), так и вашу программу (вход используется).Когда вы выводите, вы вызываете побочные эффекты, которые только изменяют состояние внешнего мира (вывод производится);сам процесс вывода не меняет состояние вашей программы.Таким образом, вы могли бы фактически сказать, что O является более «чистым», чем I.

За исключением того, что выходные данные действительно изменяют состояние выполнения вашей программы (Это не будет повторятьодна и та же операция вывода снова и снова; для того, чтобы двигаться дальше, она должна иметь какое-то изменение состояния).Все зависит от того, как вы на это смотрите.Но намного проще смешать грязь ввода и вывода в одну и ту же монаду.Любая полезная программа будет как входной, так и выходной.Вы можете разделить операции, которые вы используете, на одну или другую, но я не вижу убедительной причины использовать систему типов для этой задачи.

Либо вы связываетесь с внешним миром, либо вынет.

2 голосов
/ 01 мая 2011

Краткий ответ: IO - это не I / O. Другие люди имеют более длинные ответы, если хотите.

0 голосов
/ 27 февраля 2011

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

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

Если речь идет о информационной безопасности, было бы вполне естественно разделить чтение и запись. Но для первоначальной цели haskell, чтобы быть стандартным ленивым чисто функциональным языком, это было излишним.

...