我使用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
我觉得它可能很冗长。任何简洁的方法来处理这个?
答案 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我们为什么需要它们以及如何使用它们。