Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IO and Maybe monad interaction

Tags:

haskell

I have the following code but I feel it is too ugly and imperative. Would anybody rephrase it to be more functional? (I messed with MaybeT but could not make it work) Applicative answers welcome as well.

getString :: IO String

pred :: String -> Bool

f :: String -> String

result :: IO (Maybe String)
result = do
  s <- getString
  if pred s
    then return $ Just $ f s
    else return Nothing

EDIT: A follow-up question: what if both pred and f also return results within IO (should I split this out into a separate question?)

getString :: IO String

pred :: String -> IO Bool

f :: String -> IO String

result :: IO (Maybe String)
result = do
  s <- getString
  b <- pred s
  if b
    then Just <$> f s
    else return Nothing
like image 397
Skirmantas Kligys Avatar asked Oct 07 '11 18:10

Skirmantas Kligys


People also ask

Is IO a Monad?

Now that we also know that IO is a monad, we can wrap up the discussion we started there.

Why is IO a Monad in Haskell?

The I/O monad contains primitives which build composite actions, a process similar to joining statements in sequential order using `;' in other languages. Thus the monad serves as the glue which binds together the actions in a program.

How does IO work in Haskell?

IO is the way how Haskell differentiates between code that is referentially transparent and code that is not. IO a is the type of an IO action that returns an a . You can think of an IO action as a piece of code with some effect on the real world that waits to get executed.

Does Haskell allow side effects?

Haskell is a pure language Moreover, Haskell functions can't have side effects, which means that they can't effect any changes to the "real world", like changing files, writing to the screen, printing, sending data over the network, and so on.


2 Answers

I would begin by taking the logic here out of the IO monad. Your function can then be written as

result :: IO (Maybe String)
result = foo <$> getString

foo :: String -> Maybe String
foo s | pred s    = Just (f s)
      | otherwise = Nothing 

You could probably write foo in different ways using some fancy combinators, but I don't think that's necessary here. The most important thing is to get your logic out of IO so that it's easier to test.

like image 166
hammar Avatar answered Nov 15 '22 23:11

hammar


Here's a nice little combinator:

ensure :: MonadPlus m => (a -> Bool) -> (a -> m a)
ensure p x = guard (p x) >> return x

Now we can write a pure function which checks your predicate and applies f when appropriate:

process :: String -> Maybe String
process = fmap f . ensure pred

Lifting this to an IO action is simply another fmap:

result = fmap process getString

Personally, I'd probably inline process, and write that this way instead:

result = fmap (fmap f . ensure pred) getString

...which is a relatively clean description of what's happening.

like image 38
Daniel Wagner Avatar answered Nov 15 '22 22:11

Daniel Wagner