【问题标题】:Monads in monad transformer context单子转换器上下文中的单子
【发布时间】:2010-11-16 07:43:46
【问题描述】:

我无法理解 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(或其他 monads)并且将从中调用的错误 一元上下文(参见示例中的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

这可行,但由于绑定仍然很难看。这比以前的有大小写检查的版本要清楚得多。这是推荐的方法吗?

【问题讨论】:

    标签: haskell error-handling monads monad-transformers


    【解决方案1】:

    不清楚为什么需要ErrorT。你可以实现readEitherTlike

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

    如果你因为某种原因真的需要ErrorT,那么你可以创建实用函数eitherToErrorT

    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"
    

    【讨论】:

    • 我正在考虑例如在 ErrorT 中执行 try(foobar) ,这将在 ErrorT monad 中传播可能的错误。 (IO(任一个))
    • 我添加了一个如何使用ErrorT 传播错误的示例,也许它会有所帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    • 2013-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多