Ответ 1
Рассмотрим определение MonadPlus
:
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
Как бы вы реализовали mzero
для IO
? Значение типа IO a
представляет собой вычисление IO, которое возвращает что-то типа a
, поэтому mzero
должно быть вычислением ввода-вывода, возвращающим что-то из любого возможного типа. Ясно, что нет способа вызвать значение для какого-либо произвольного типа, и в отличие от Maybe
нет никакого "пустого" конструктора, который мы можем использовать, поэтому mzero
обязательно будет представлять собой вычисление IO, которое никогда не возвращается.
Как вы пишете вычисление ввода-вывода, которое никогда не возвращается? Либо переходите в бесконечный цикл, либо в основном выполняйте ошибку времени выполнения. Первый имеет сомнительную полезность, поэтому последнее - это то, с чем вы застряли.
Короче говоря, чтобы написать экземпляр MonadPlus
для IO
, что бы вы сделали, это следующее: Have mzero
вывести исключение во время выполнения и иметь mplus
оценить свой первый аргумент, вылавливая любые исключения, mzero
. Если никаких исключений не возникает, верните результат. Если возникает исключение, оцените второй аргумент mplus
, игнорируя исключения.
Тем не менее, исключения во время выполнения часто считаются нежелательными, поэтому я бы смутился, прежде чем идти по этому пути. Если вы действительно хотите сделать это таким образом (и не возражаете увеличить вероятность того, что ваша программа может упасть во время выполнения), вы найдете все, что вам нужно, чтобы реализовать выше в Control.Exception
.
На практике я, вероятно, либо использовал бы принцип трансформации монады, если бы захотел много guard
ing на результат оценки монодичных выражений, или если большинство условных выражений зависят от чистых значений, предоставляемых как аргументы функции (которые флаги в вашем примере) используют защитные элементы шаблонов, как в ответе @Anthony.