MonadPlus definition for Haskell IO

后端 未结 3 455
独厮守ぢ
独厮守ぢ 2021-01-04 06:17

I was just writing a quick bit of code, and I wanted to use the guard function in the IO Monad. However, there is no definition of MonadPlus for IO which means that we canno

相关标签:
3条回答
  • 2021-01-04 07:01

    I do this sort of thing with guards.

    handleFlags :: [Flag] -> IO ()
    handleFlags flags
      | Help `elem` flags = putStrLn "Usage: program_name options..."
      | otherwise = return ()
    
    0 讨论(0)
  • 2021-01-04 07:11

    Consider the definition of MonadPlus:

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

    How would you implement mzero for IO? A value of type IO a represents an IO computation that returns something of type a, so mzero would have to be an IO computation returning something of any possible type. Clearly, there's no way to conjure up a value for some arbitrary type, and unlike Maybe there's no "empty" constructor we can use, so mzero would necessarily represent an IO computation that never returns.

    How do you write an IO computation that never returns? Either go into an infinite loop or throw a runtime error, basically. The former is of dubious utility, so the latter is what you're stuck with.

    In short, to write an instance of MonadPlus for IO what you'd do is this: Have mzero throw a runtime exception, and have mplus evaluate its first argument while catching any exceptions thrown by mzero. If no exceptions are raised, return the result. If an exception is raised, evaluate mplus's second argument instead while ignoring exceptions.

    That said, runtime exceptions are often considered undesirable, so I'd hesitate before going down that path. If you do want to do it that way (and don't mind increasing the chance that your program may crash at runtime) you'll find everything you need to implement the above in Control.Exception.

    In practice, I'd probably either use the monad transformer approach if I wanted a lot of guarding on the result of evaluating monadic expressions, or if most of the conditionals depend on pure values provided as function arguments (which the flags in your example are) use the pattern guards as in @Anthony's answer.

    0 讨论(0)
  • 2021-01-04 07:14

    There're functions precisely made for this: in Control.Monad, the functions when and its counterpart unless. Anthony's answer can be rewritten as such:

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

    specifications:

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

    Link to docs on hackage.haskell.org

    If more is needed, here's a link to another package, specifically monad-oriented and with several more utilities: Control.Monad.IfElse

    0 讨论(0)
提交回复
热议问题