如果你尝试计算并不成功,那么你会得到一个异常:Exception e => EitherT a IO (Either e ())
。因此,你应该避免这样的情况发生。
testEx :: (Exception e, MonadTrans m) => m IO (Either e ())
testEx = lift . try $ fails
您希望将错误集成到EitherT
中,而不是在结果中产生错误。您希望将try
与EitherT
结合使用。
testEx :: (Exception e) => EitherT e IO ()
testEx = EitherT . try $ fails
我们将首先进行总体概述,然后仅获取您想要的消息。
使用EitherT集成try
您可以提取将try
与EitherT
集成的思想。
tryIO :: (Exception e) => IO a -> EitherT e IO a
tryIO = EitherT . try
或者对于任何底层的
MonadIO
,您可以这样做
tryIO :: (Exception e, MonadIO m) => IO a -> EitherT e m a
tryIO = EitherT . liftIO . try
(tryIO
与 Control.Error
中的一个名称冲突。我无法为此想出另一个名称。)
您可以表示愿意捕获任何异常。SomeException
将捕获所有异常。如果您只对特定的异常感兴趣,请使用不同的类型。有关详细信息,请参见Control.Exception。如果您不确定要捕获什么,您可能只想捕获 IOException
;这就是来自 Control.Error
的 tryIO
所做的事情;请参见最后一节。
anyException :: EitherT SomeException m a -> EitherT SomeException m a
anyException = id
你只想从异常中保留错误信息。
message :: (Show e, Functor m) => EitherT e m a -> EitherT String m a
message = bimapEitherT show id
那么您可以编写代码:
然后您可以编写
testEx :: EitherT String IO ()
testEx = message . anyException . tryIO $ fails
将try与MonadError集成
您可以使用MonadError
和MonadIO
渗透转换器堆栈,将任何MonadError
与try
集成。
import Control.Monad.Except
tryIO :: (MonadError e m, MonadIO m, Exception e) => IO a -> m a
tryIO = (>>= either throwError return) . liftIO . try
您可以利用上一节中的 tryIO
, anyException
, 和 message
,编写出 testEx
相关的代码。
testEx :: EitherT String IO ()
testEx = message . anyException . tryIO $ fails
来自Control.Error的tryIO函数
tryIO
函数来自于 Control.Error 库,与我们的第一个 tryIO
函数类似,只是它只捕获 IOException
异常。其实函数定义如下:
tryIO :: (MonadIO m) => IO a -> EitherT IOException m a
tryIO = EitherT . liftIO . try
我们可以使用
message
来编写
testEx
,并将其输出。
testEx :: EitherT String IO ()
testEx = message . tryIO $ fails
lifted-base
似乎是你正在寻找的,我是通过这篇文章发现的。 - bheklilr