MonadPlus определение для Haskell IO - PullRequest
12 голосов
/ 22 декабря 2010

Я просто писал небольшой кусочек кода и хотел использовать функцию guard в монаде ввода-вывода. Тем не менее, нет определения MonadPlus для IO , что означает, что мы не можем использовать охрану на земле IO. Я видел пример использования преобразователя MabyeT для использования защиты в Maybe Monad , а затем отмены всех действий ввода-вывода, но я действительно не хочу этого делать, если мне это не нужно.

Примером того, что я хочу, может быть:

handleFlags :: [Flag] -> IO ()
handleFlags flags = do
    when (Help `elem` flags) (putStrLn "Usage: program_name options...")
    guard (Help `elem` flags)
    ... do stuff ...
    return ()

Мне было интересно, есть ли хороший способ получить функцию защиты (или что-то подобное) в монаде ввода-вывода через объявление для MonadPlus или иным образом. Или, возможно, я делаю это неправильно; Есть ли лучший способ написать это сообщение помощи в функции выше? Спасибо.

(П.С. Я мог бы использовать операторы if-then-else, но, похоже, это как-то опровергает эту точку. Не говоря уже о том, что для многих вариантов это приведет к огромному количеству вложений.)

Ответы [ 3 ]

22 голосов
/ 22 декабря 2010

Рассмотрим определение MonadPlus:

class Monad m => MonadPlus m where
    mzero :: m a 
    mplus :: m a -> m a -> m a

Как бы вы реализовали mzero для IO?Значение типа IO a представляет вычисление IO, которое возвращает что-то типа a, поэтому mzero должно быть вычислением IO, возвращающим что-то любого возможного типа.Ясно, что нет никакого способа вызвать в воображении значение для некоторого произвольного типа, и в отличие от Maybe нет никакого «пустого» конструктора, который мы можем использовать, поэтому mzero будет обязательно представлять вычисление ввода-вывода, которое никогда не вернет .

Как вы пишете вычисления ввода-вывода, которые никогда не возвращаются?Либо перейдите в бесконечный цикл, либо, в принципе, сгенерируйте ошибку времени выполнения.Первый имеет сомнительную полезность, поэтому последний - то, с чем вы застряли.

Короче говоря, чтобы написать экземпляр MonadPlus для IO, вы должны сделать следующее: Иметь mzero сгенерировать исключение во время выполнения и mplus оценить его первый аргумент, перехватывая любые исключения, выданные mzero.Если исключений нет, верните результат.Если возникает исключение, вместо этого оцените второй аргумент mplus, игнорируя при этом исключения.

При этом исключения во время выполнения часто считаются нежелательными, поэтому я буду колебаться, прежде чем идти по этому пути.Если вы действительно хотите сделать это таким образом (и не возражаете против увеличения вероятности сбоя вашей программы во время выполнения), вы найдете все, что вам нужно для реализации вышеизложенного, в Control.Exception.

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

8 голосов
/ 22 декабря 2010

Я делаю такие вещи с охраной.

handleFlags :: [Flag] -> IO ()
handleFlags flags
  | Help `elem` flags = putStrLn "Usage: program_name options..."
  | otherwise = return ()
0 голосов
/ 01 мая 2016

Для этого точно созданы функции: в Control.Monad функции when и его аналог unless.Ответ Энтони можно переписать так:

handleFlags :: [Flag] -> IO ()
handleFlags flags =
    when (Help `elem` flags) $ putStrLn "Usage: program_name options..."

Характеристики:

when :: (Applicative m) => Bool -> m () -> m ()
unless bool = when (not bool)

Ссылка на документы на hackage.haskell.org

Еслитребуется больше, вот ссылка на другой пакет, специально ориентированный на монады и с несколькими дополнительными утилитами: Control.Monad.IfElse

...