Search code examples
haskellmonadsio-monad

how to use IO Monad in another Monad


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 ?


Solution

  • 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)