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

Почему у Haskell нет я Monad (только для ввода, в отличие от монады IO)?

Понятно, что вычисление, выполняющее выходные данные, сильно отличается от того, которое выполняет только вход. Последнее в некотором смысле намного чище.

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

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

Любая причина, по которой это не сработает, чтобы иметь монаду я (и O Monad, которая может быть объединена в IO Monad)?

Изменить. В основном я подразумевал ввод как чтение файлов, а не взаимодействие с пользователем. Это также мой прецедент, где я могу предположить, что входные файлы не изменяются во время выполнения программы (в противном случае это нормально для поведения undefined).

4b9b3361

Ответ 1

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

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

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

Основные идеи о том, как вы можете комбинировать разные монады, описаны в документе Data Types a la Carte (отличное чтение, очень влиятельное, не может рекомендовать достаточно, и т.д.).

Ответ 2

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

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

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

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

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

runI :: I Identity r -> IO r

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

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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

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