2010-11-16 69 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