在do部分中使用ErrorT变换器monad

时间:2016-04-20 13:57:18

标签: haskell error-handling monads

我想了解如何在do部分处理堆栈的转换器。它也与错误处理有关。

即,我会告诉你我的问题 我有一些功能:

foo :: Int -> String -> IO (Either String (Value, Integer))

foo的身体在这里并不重要。

ef :: evalExpr :: Int -> String ->  IO (Either String (Value, Integer))

现在,

f :: Integer -> String -> StateT Integer (ReaderT Integer (ErrorT String IO)) ()
f i s = do
  vl <- lift $ lift $ lift $ ef i s
  ....
  return ()

ef可以调用throwError。它也可以返回(Value, String) 我希望能够(....)做一些正确的值(所以(Value, String))。但是,我不能这样做。这是我的永久性问题。

想法如下:
ef调用throwError时,foo应该传播相同的错误(所以Left String)。从来没有,ef返回Right Value (so (Value, String))我想使用它并在此基础上做出决定应该返回foo - throwError(Value, ())

我了解fmap<*>。 你能帮我吗 ?

1 个答案:

答案 0 :(得分:2)

功能:

ef :: Int -> String ->  IO (Either String (Value, Integer))

只能throwError IOException

instance MonadError IOException IO where
    throwError = ioError
    catchError = catch

否则,它只是IO函数返回Either String (Value, Integer)

鉴于该函数,要对其值进行操作,您需要在具有IO功能的上下文中执行它,然后只使用结果值:

callEf :: IO ()
callEf = do
    result <- ef ...
    case result of
        Left s ->         -- do something with the String
        Right (v, i) ->   -- do something with the tuple

    return ()
    -- ^ you can also obviously produce the value above,
    -- if you want something more complicated than ()

如果你在monad堆栈中,这不会改变。在您的函数f中,调用lift三次会为您提供IO上下文。之后,您可以使用结果值执行任何操作,包括堆栈中的任何操作,例如throwError(在两次提升之后)或put(没有提升)。

或者,请参阅我之前问题的答案,以摆脱lift

如果您希望错误传播从Either自动发生到ErrorT,那么它就会非常简单,就像上面一样:

eitherToError :: Either e v -> ErrorT e m v
eitherToError f = case f of
    Left err -> throwError err
    Right val -> return val

您可以easily search for in Hayoo,在某些图书馆中生成in a very similar implementation

尽管如此,ErrorT似乎已弃用Control.Monad.Trans.Except