haskell – Monad在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函数,但我无法弄清楚如何.如果我试着解除
readEither在readEitherT函数中,它将其提升为ErrorT String IO
(要么是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

这有效,但由于绑定仍然很难看.这比以前版本的案例检查要清晰得多.这是推荐的方法吗?

最佳答案 目前尚不清楚为什么需要ErrorT.你可以实现readEitherT之类的

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

如果由于某种原因确实需要ErrorT,那么你可以创建效用函数toToErrorT:

eitherToErrorT = ErrorT . pure

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

[加]
也许你只想将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"
点赞