2015-04-02 101 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