Reputation: 1047
I use MongoDB library to handle data from Mongodb. There is a Monad called Action
representing a DB read or write operation https://github.com/TonyGen/mongoDB-haskell/blob/master/doc/tutorial.md
.
But, I find that when I in monad Action, I also want to do some IO which must be in an IO Monad. Some codes like
-- `Action' is a Monad
--
intoFile :: String -> Cursor -> Action IO ()
intoFile ric c = do
outH <- liftIO $ openFile ric AppendMode
liftIO $ hPutStrLn outH "Some log"
loopIntoFile outH c
liftIO $ hClose outH
There is a liftIO
before any IO
monad and I think it may be verbose. Any concise way to handle this ?
Upvotes: 0
Views: 740
Reputation: 55069
You can’t avoid the liftIO
, unfortunately, because the standard IO
actions are not overloaded to work in any MonadIO
. But you can join sequences of IO
actions under one call to liftIO
:
intoFile :: String -> Cursor -> Action IO ()
intoFile ric c = do
outH <- liftIO $ do
openFile ric AppendMode
hPutStrLn outH "Some log"
loopIntoFile outH c
liftIO $ hClose outH
Or, if you intend to use the same IO
operations repeatedly, you can introduce auxiliary definitions for them:
intoFile :: String -> Cursor -> Action IO ()
intoFile ric c = do
outH <- openLog ric AppendMode
log outH "Some log"
loopIntoFile outH c
closeLog outH
openLog path mode = liftIO (openFile path mode)
log handle message = liftIO (hPutStrLn handle message)
closeLog handle = liftIO (hClose handle)
Upvotes: 3
Reputation: 176
You want to carry along 2 additional contexts - the IO
context and Action
context. That's the case for monad transformers, because they allow you to deal with layering monads and choose inside of do
block which monad to choose for desired action. Here is a great explanation of why we need them and how to use them.
Upvotes: 0