我想了解如何在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
和<*>
。
你能帮我吗 ?
答案 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
。