我无法抓住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函数,但我无法弄清楚如何。如果我试着解除
readEither在readEitherT函数中,它将它提升到ErrorT String IO
(Either String Int)
。但我应该以某种方式把它带到ErrorT
String IO Int
。
如果我走错了方向,那么正确的方法是什么
处理需要IO(或其他monad)并从中调用的错误
monadic context(参见示例中的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
这有效,但由于绑定仍然很难看。这比以前版本的案例检查要清晰得多。这是推荐的方法吗?
答案 0 :(得分: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"