Emm*_*ery 12 haskell monad-transformers
我正在使用EitherT monad变压器.将它与IO monad相结合,恐怕我会得到一个异常并且它不会被捕获.
事实上,例外只是通过:
import Control.Monad.Trans
import Control.Error
import System.Directory
main = runEitherT testEx >>= print
testEx :: EitherT String IO ()
testEx = lift $ removeFile "non existing filename"
Run Code Online (Sandbox Code Playgroud)
但是EitherT
否则完全符合法案以向呼叫者传达错误.所以我想使用它,而不是抛出异常......
我try
从Control.Exception 看了一下:
try :: Exception e => IO a -> IO (Either e a)
Run Code Online (Sandbox Code Playgroud)
它看起来正是我想要的,它将适合我的EitherT IO堆栈...(可能有一个添加hoistEither
,也许fmapL
它开始看起来很冗长)但是一个天真的lift $ try
不是类型检查.
我确信这个问题已经解决了好几千次,但我找不到任何描述这个问题的好链接.这应该如何解决?
编辑通过"这应该如何解决",我对惯用解决方案感兴趣,在haskell中处理它的标准方法是什么.从目前为止的答案来看,似乎惯用的方法是抛出异常并将其处理得更高.看起来有点反直觉,有两个控制和返回路径流,但它显然是它的意图.
Mic*_*man 16
我认为EitherT
这不是正确的做法.你想说的是" IO
是副作用,EitherT
是异常." 但事实并非如此:IO
总是有可能导致异常,所以你所做的就是为你的API添加一种错误的安全感,并引入两种方式来抛出异常而不是一种异常.此外,不是使用SomeException
优先考虑的良好结构,而是IO
减少到String
丢弃信息.
无论如何,如果你确信这是你想要做的,那就不难了.它看起来像:
eres <- liftIO $ try x
case eres of
Left e -> throwError $ show (e :: SomeException)
Right x -> return x
Run Code Online (Sandbox Code Playgroud)
但请注意,这也会吞噬异步异常,这通常不是您想要做的.我认为更好的方法是封闭 - 例外.
你不想进行lift
try
计算,那么你就得到了一个Exception e => EitherT a IO (Either e ())
.
testEx :: (Exception e, MonadTrans m) => m IO (Either e ())
testEx = lift . try $ fails
Run Code Online (Sandbox Code Playgroud)
您不希望结果中出现错误,您希望将错误集成到EitherT
.你想要将try
某些东西与你的东西融为一体EitherT
testEx :: (Exception e) => EitherT e IO ()
testEx = EitherT . try $ fails
Run Code Online (Sandbox Code Playgroud)
我们一般会这样做,然后得到你想要的信息.
您可以提取相结合的理念try
与EitherT
tryIO :: (Exception e) => IO a -> EitherT e IO a
tryIO = EitherT . try
Run Code Online (Sandbox Code Playgroud)
或者,对于任何潜在MonadIO
的
tryIO :: (Exception e, MonadIO m) => IO a -> EitherT e m a
tryIO = EitherT . liftIO . try
Run Code Online (Sandbox Code Playgroud)
(tryIO
与姓名冲突Control.Error
.我无法为此提出另一个名字.)
然后你可以说你愿意抓住任何例外.SomeException
将捕获所有异常.如果您只对特定例外感兴趣,请使用其他类型.有关详细信息,请参见Control.Exception.如果你不确定你想要捕获什么,你可能只想抓住IOException
s; 这是tryIO
从Control.Error
做; 见最后一节.
anyException :: EitherT SomeException m a -> EitherT SomeException m a
anyException = id
Run Code Online (Sandbox Code Playgroud)
您只想保留异常的错误消息
message :: (Show e, Functor m) => EitherT e m a -> EitherT String m a
message = bimapEitherT show id
Run Code Online (Sandbox Code Playgroud)
然后你就可以写了
testEx :: EitherT String IO ()
testEx = message . anyException . tryIO $ fails
Run Code Online (Sandbox Code Playgroud)
您可以将try
某些东西与任何东西集成MonadError
,使用MonadError
和MonadIO
穿透变压器堆栈.
import Control.Monad.Except
tryIO :: (MonadError e m, MonadIO m, Exception e) => IO a -> m a
tryIO = (>>= either throwError return) . liftIO . try
Run Code Online (Sandbox Code Playgroud)
你可以写testEx
在这方面tryIO
并anyException
与message
从上一节
testEx :: EitherT String IO ()
testEx = message . anyException . tryIO $ fails
Run Code Online (Sandbox Code Playgroud)
在tryIO
从Control.Error基本上是我们的第一次tryIO
,但它只能抓住IOException
s,而不是任何异常.它实际上被定义为
tryIO :: (MonadIO m) => IO a -> EitherT IOException m a
tryIO = EitherT . liftIO . try
Run Code Online (Sandbox Code Playgroud)
我们可以用它message
来写testEx
的
testEx :: EitherT String IO ()
testEx = message . tryIO $ fails
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3689 次 |
最近记录: |