Функция похожа на (>> =), но возвращает другую монаду - PullRequest
9 голосов
/ 23 декабря 2011

Тип (>>=):

(>>=) :: Monad m => m a -> (a -> m b) -> m b

Мне нужна функция, которая имеет тип:

(Monad m, Monad n) => m a -> (a -> n b) -> n b

Эта функция может использоваться для объединения различных монад.

Я столкнулся с этой проблемой, когда пытался получить 3000 из аргументов командной строки -p 3000:

main = getArgs >>= (\args -> (elemIndex "-p" args) >>= (\id -> warpDebug (fromIntegral.read (args !! (id+1))) Ilm))

Это явно не скомпилируется, поскольку getArgs возвращает IO [String] и elemIndex возвращает Maybe Int.Функция указанного выше типа может быть использована для элегантного решения этой проблемы.Мой вопрос:

  • Эта функция уже определена?( Hoogle не находит)
  • Если нет, возможно, по какой-то причине.В чем причина тогда?Это считается плохой практикой?Я думаю, что это лучший способ, чем использование выражения case.

Ответы [ 3 ]

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

Такой функции не существует. Фактически, если вы возьмете n в качестве монады идентификации, это позволит вам создать функцию m a -> a, которая явно не может быть определена для всех монад.

Чтобы решить общую проблему «составления» двух монад, вы можете взглянуть на монадные трансформаторы .

Однако использование монадных трансформаторов в вашем примере кажется излишним. Вы можете просто определить функцию [String] -> Maybe Args (для некоторого пользовательского типа Args - скажем, Int в примере), которая выполняет обработку аргументов командной строки, затем сопоставить шаблон с результатом (или использовать maybe) из монады IO.

7 голосов
/ 23 декабря 2011

Эта функция не существует, потому что она не будет иметь смысла для всех монад. По сути, это эквивалентно функции распаковки монады Monad m => m a -> a - единственное отличие состоит в том, что вы немедленно помещаете ее в другую монаду.

Причина, по которой эта функция не определена для всех монад, заключается в том, что она не имеет смысла для некоторых из них. Например, возьмите Maybe: единственный способ распаковать это - выдать ошибку, если у вас есть Nothing, а ошибки времени выполнения игнорируются. Более крайним примером будет IO - использование функции, которая может «распаковать» значения IO, приведет к странному и потенциально недетерминированному поведению.

Таким образом, у вас вообще нет такой функции. Тем не менее, многие специфические монады do поставляются с такими функциями. Отличным примером является runST; это на самом деле безопасный способ иметь дело с государством. На самом деле у вас do есть такие функции как для Maybe, так и для IO (fromJust и unsafePerformIO соответственно), но у них есть проблемы, которые я описал выше, и вам следует их избегать.

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

В вашем случае, если вы абсолютно уверены, что Maybe никогда не будет Nothing, используйте fromJust. Однако, как правило, это не очень хорошая практика, поэтому вы должны просто придерживаться шаблона, соответствующего значению из Maybe.

2 голосов
/ 23 декабря 2011

Ответ зависит от того, нужно ли вам использовать монады Maybe и IO вместе или по отдельности.

Если вам нужно использовать их вместе - ответ заключается в том, что вам нужно составить монады IO и Maybeпутем построения стека монадных трансформаторов, содержащих монаду IO и монадный трансформатор MaybeT.

Если вам нужны они отдельно, тогда работает более простое решение:

import System.Environment
import Data.List

main = getArgs >>= (\args -> return (elemIndex "-p" args 
    >>= \y -> return $ y + 900) >>= print)

Обратите внимание на return.Таким образом, у вас есть Maybe монада во внутренних скобках (между elemIndex и 900), но не IO.То есть вы не можете выполнять действия ввода-вывода до выхода из монады Maybe, как я показал с печатью.

...