2015-04-02 48 views
0

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

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

一些代码有任何IO单子前liftIO,我想这可能是冗长。任何简洁的方式来处理这个?

+2

有ISN 'IO'操作完全是作为返回'IO a'而不是'MonadIO m => ma'来实现的,所以不是一个好办法。我经常在函数中看到'where io = liftIO'这样的东西,并且使用函数'io'代替'liftIO',只是为了减少输入。理想情况下,我们有'hPutStrLn :: MonadIO m => Handle - > String - > m()',但是我们不能期望'base'的函数依赖于第三方库中定义的类(即使这些库是haskell平台的一部分)。 – bheklilr 2015-04-02 14:03:57

+1

如果你有一系列的IO动作,你可以改变'do liftIO action1; liftIO action2'到'liftIO $做action1; action2'来保存一些电梯。 – chi 2015-04-02 14:26:00

回答

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

您希望携带2个附加上下文 - IO上下文和Action上下文。对于monad变压器,情况就是这样,因为它们允许您处理分层monad,并在do区块内部选择monad以选择所需操作。 Here is a great explanation为什么我们需要它们以及如何使用它们。

+0

我建议你仔细阅读这个问题。 – dfeuer 2015-04-03 06:09:00