Robert Onslow
Robert Onslow

Reputation: 215

Composing IO Monads using do

I have code in the Reader Monad, so as to pass a file handle as an invisible parameter down the Reader chain.

In writeMail, I am trying to create a Reader, which, when run using runReader, produces an IO () output which is itself the result of a chain of IO monads

writeMail :: Reader Handle (IO ())
writeMail mail = do
 wmh <- writeMailHeaders mail
 wmb <- writeMailBody mail
return $ wmh >>= \_ -> wmb

However I am finding that only the last in the IO chain i.e. wmb, prints at the console.

Can anyone see what I should be doing to get wmh, then wmb to print?

Upvotes: 0

Views: 182

Answers (2)

phadej
phadej

Reputation: 12123

With simpler example:

module Read where

import Data.Functor.Identity

write :: Monad m => m (IO ())
write = do
  a <- return $ putStrLn "foo"
  b <- return $ putStrLn "bar"
  return $ a >> b

main :: IO ()
main = runIdentity write

main prints both "foo" and "bar". So I suspect the error is in writeMailHeaders.

Upvotes: 3

Nikita Volkov
Nikita Volkov

Reputation: 43309

What you need is not just a reader, but a ReaderT monad transformer with IO as a base monad.

Since your example was incomplete, I made some changes to show your options:

import Control.Monad.Reader

writeMail :: ReaderT Handle IO ()
writeMail = do

  -- Here's how you get your handle to further do something to it:
  handle <- ask

  -- Here's how you do the IO actions.
  -- Notice the `lift` function, 
  -- which allows us to run actions of the base monad,
  -- which in that case is `IO`.
  lift $ do
    print "bla bla"
    print "bla"

Upvotes: 2

Related Questions