如何在另一个Monad中使用IO Monad

时间:2015-04-02 13:35:14

标签: haskell monads io-monad

我使用MongoDB库来处理来自Mongodb的数据。有一个名为Action的Monad表示数据库读取或写入操作https://github.com/TonyGen/mongoDB-haskell/blob/master/doc/tutorial.md。 但是,我发现当我在monad Action中时,我也想做一些必须在IO Monad中的IO。一些代码如

-- `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

在任何liftIO monad之前有一个IO我觉得它可能很冗长。任何简洁的方法来处理这个?

2 个答案:

答案 0 :(得分:3)

遗憾的是,您无法避免使用liftIO,因为标准的IO操作不会超载,无法在任何MonadIO中使用。但是,您可以在IO的一次调用中加入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

或者,如果您打算重复使用相同的IO操作,可以为它们引入辅助定义:

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)

答案 1 :(得分:0)

您想要携带2个额外的上下文 - IO上下文和Action上下文。这就是monad变换器的情况,因为它们允许你处理分层monad并在do块中选择monad来选择所需的动作。 Here is a great explanation我们为什么需要它们以及如何使用它们。