2017-05-08 35 views
3

我想寫一個haskell程序閱讀配置,並做一些事情。該配置是一個Data.Map並將被注入一個閱讀器。當我們找不到配置項時,讀數應該被中斷。剛剛停止,不需要錯誤消息。所以我只想要一個Maybe monad(不是monad)。使用ReaderT Maybe或MaybeT Reader?

問題是,我應該如何堆疊兩個monads,ReaderT MaybeMaybeT Reader

回答

4

最後我弄明白了。他們都是好的。

假設每個配置項目的值只是一個Int。

ReaderT ConfigMap Maybe Int將具有象一個值:

ReaderT (\r -> Just 123) 

ReaderT (\r -> Nothing) 

MaybeT (Reader ConfigMap) Int將具有象一個值:

MaybeT (Reader (\r -> Just 123)) 

或:

MaybeT (Reader (\r -> Nothing)) 

無論如何,我們可以先做一些閱讀,然後決定是否繼續根據閱讀是否返回Nothing。這些值具有不同的外形但功能相同。

我的小演示在這裏:

import qualified Data.Map as M 
import Control.Monad 
import Control.Monad.Reader 
import Control.Monad.Trans 
import Control.Monad.Trans.Maybe 

type Config = M.Map String Int 

getConfig :: String -> MaybeT (Reader Config) Int 
getConfig key = MaybeT $ do 
    m <- ask 
    return $ M.lookup key m 

readAll :: Config -> Maybe (Int, Int) 
readAll m = 
    let r = runMaybeT $ do 
     a <- getConfig "a" 
     b <- getConfig "b" 
     return (a, b) 
    in runReader r m 

main :: IO() 
main = do 
    putStrLn $ show (readAll $ M.fromList [("x", 3), ("b", 4)]) 

我的第二個演示:

import qualified Data.Map as M 
import Control.Monad 
import Control.Monad.Reader 
import Control.Monad.Trans 
import Control.Monad.Trans.Maybe 

type Config = M.Map String Int 

getConfig :: String -> ReaderT Config Maybe Int 
getConfig key = ReaderT $ \r -> M.lookup key r 

readAll :: Config -> Maybe (Int, Int) 
readAll m = 
    let r = runReaderT $ do 
     a <- getConfig "a" 
     b <- getConfig "b" 
     return (a, b) 
    in r m 

main :: IO() 
main = do 
    putStrLn $ show (readAll $ M.fromList [("a", 3), ("b", 4)]) 
+2

如果展開的類型,而'newtype'構造函數,你會發現他們都展開到的r - >也許',這是一個很好的暗示,他們可能是相同的。但是在某些情況下,monad操作符即使具有相同的類型也會有不同的行爲,例如, 'ListT(狀態s)'和'StateT s []'。 (這是因爲'ListT'有點破了......但是在這種情況下就是一個很好的例子!) – luqui

+1

我發現'ReaderT Maybe'和'MaybeT Reader'確實是一對特殊的對。而像這樣的一對是相同的「內部形狀」(除去所有類型的構造函數和monad值的lambda('\ a - >')之後的剩餘部分)。順便說一下,對於一個可能失敗的日誌計算,'MaybeT Writer'和'WriterT Maybe'可能會有所不同:前者會以'(Nothing,「前N行日誌)失敗,而後者只會以'Nothing '。 – brk