2010-11-16 68 views
4

我無法抓住monads和monad變壓器。我有 以下人爲的例子(未編譯):monad在monad變壓器上下文

import Control.Monad 
import Control.Monad.Error 
import Control.Monad.Reader 

data State = State Int Int Int 
type Foo = ReaderT State IO 

readEither :: String -> Either String Int 
readEither s = let p = reads s 
      in case p of 
       [] -> throwError "Could not parse" 
       [(a, _)] -> return a 

readEitherT :: IO (Either String Int) 
readEitherT = let p s = reads s 
      in runErrorT $ do 
    l <- liftIO (getLine) 
    readEither l 

foo :: Foo Int 
foo = do 
    d <- liftIO $ readEitherT 
    case d of 
     Right dd -> return dd 
     Left em -> do 
    liftIO $ putStrLn em 
    return (-1) 

bar :: Foo String 
bar = do 
    liftIO $ getLine 

defaultS = State 0 0 0 

如果我複製readEither到readEitherT的功能,它的工作原理,但我 有一種揮之不去的感覺,我可以充分利用現有的 readEither的力量功能,但我無法弄清楚如何。如果我嘗試解除readEitherT函數中的 readEither,它會將其解除爲應有的ErrorT String IO (Either String Int)。但我應該得到它ErrorT String IO Int

如果我要去的方向錯了這一點,什麼是正確的方法需要IO(或其他單子),並從 單子上下文中調用 手柄錯誤(見foo函數的例子)

編輯: 顯然它並不清楚我正在嘗試做什麼。也許下面的函數說明什麼,爲什麼我想知道

maybePulseQuit :: Handle -> IO (Either String()) 
maybePulseQuit h = runErrorT $ do 
    f <- liftIO $ (communicate h "finished" :: IO (Either String Bool)) 
    (ErrorT . pure) f >>= \b → liftIO $ when b $ liftIO pulseQuit 

這工作,但由於結合的還難看。這比之前有案例檢查的版本要清晰得多。這是推薦的方式嗎?

回答

2

目前尚不清楚爲什麼您需要ErrorT。您可以實現readEitherT

readEitherT :: IO (Either String Int) 
readEitherT = fmap readEither getLine 

如果你真的需要ErrorT出於某種原因,那麼你就可以創建效用函數eitherToErrorT

eitherToErrorT = ErrorT . pure 

readEitherT = runErrorT $ do 
    l <- liftIO $ getLine 
    eitherToErrorT $ readEither l 

[ADD] 也許你只是想添加ErrorT成你的monad堆棧...

data State = State Int Int Int 
type Foo = ErrorT String (ReaderT State IO) 

runFoo :: Foo a -> State -> IO (Either String a) 
runFoo foo s = runReaderT (runErrorT foo) s 

doIt :: Int -> Foo Int 
doIt i = if i < 0 
      then throwError "i < 0" 
      else return (i * 2) 

例如:

*Main> runFoo (doIt 1 >>= doIt) (State 0 0 0) 
Right 4 
*Main> runFoo (doIt (-1) >>= doIt) (State 0 0 0) 
Left "i < 0" 
+0

我在想,例如在ErrorT中嘗試(foobar),它會在ErrorT monad中傳播可能的錯誤。 (IO(或e)) – Masse 2010-11-16 13:11:28

+0

我已經添加了一個示例如何傳播錯誤使用'ErrorT',也許它會幫助 – Yuras 2010-11-16 15:02:53