Haskell 为什么不是';t'catch'捕获此异常?

Haskell 为什么不是';t'catch'捕获此异常?,haskell,Haskell,我有一个服务应用程序和一个端点,它在数据库中创建一个记录,然后尝试在S3位置之间复制一个文件。如果复制失败,我想回滚事务。我有这个接线员 {-# LANGUAGE TemplateHaskell #-} import Control.Monad.Catch import Control.Monad.Except import Control.Monad.Logger (<??) :: (MonadError e m, MonadCatch m, MonadLogger m)

我有一个服务应用程序和一个端点,它在数据库中创建一个记录,然后尝试在S3位置之间复制一个文件。如果复制失败,我想回滚事务。我有这个接线员

{-# LANGUAGE TemplateHaskell #-}    

import Control.Monad.Catch
import Control.Monad.Except
import Control.Monad.Logger

(<??)
  :: (MonadError e m, MonadCatch m, MonadLogger m)
  => e
  -> m a
  -> m a
(<??) err a = a `catchAll` (\e -> $(logErrorSH) e >> throwError err)
infixr 0 <??

为了测试逻辑,我移动了我的AWS凭证文件,希望内部AWS操作会抛出一个,然后
(如果你的
应用程序
类型最终是一个,问题可能是
异常
MonadError
MonadCatch
实例不匹配:

  • MonadError
    实例在
    ExceptT e
  • MonadCatch
    intance捕获底层monad中的异常,而不是
    ExceptT e
    错误
MonadCatch(不包括e m)
的代码如下:

-- | Catches exceptions from the base monad.
instance MonadCatch m => MonadCatch (ExceptT e m) where
   catch (ExceptT m) f = ExceptT $ catch m (runExceptT . f)
有一个实例,因此可以将其作为两个实例抛出


Edit:“exceptions”类
MonadMask
提供了一个功能,即使对于
ExceptT
,它也表现得相当好:它在
ExceptT e
异常和常规异常的情况下运行清理操作:

仅在主操作中引发错误时运行操作。与 一个例外,这适用于所有类型的错误,而不仅仅是异常。 例如,如果f是一个异常计算,它以左键终止, 计算onError f g将执行g,而onException f g将执行g 不是


处理回滚时,这是一个比catch更好的选择。

Hmm,好吧。我想我明白了。我的
App a
是一个
ReaderT配置(LoggingT(除了ServantErr IO))一个
,其中包含
MonadError
MonadCatch
MonadThrow
的实例。我是否需要为其中任何一个或所有实例编写自定义实例?或者我如何才能确保在抛出异常的monad中捕获异常?也许您可以修改
(感谢您的额外编辑!我将给
onError
一个镜头。因此,在我的处理程序中使用
onError
而不是
catch
,似乎确实可以做到这一点。不过,我确实同意,现在我考虑了更多,混合
MonadError
MonadThrow/catch
似乎是错误的(一开始就造成了这种混乱)。但是,如果我将我的
throwError
s更改为
throwM
,这会导致错误不会传播回客户端,例如,引发异常,响应为500,例如,如果我使用
throwError err400
,则会创建正确的响应。使用
throwM
会更好吗Y在处理程序中的何处,
catch
error,然后对其使用
throwerr
-- | Catches exceptions from the base monad.
instance MonadCatch m => MonadCatch (ExceptT e m) where
   catch (ExceptT m) f = ExceptT $ catch m (runExceptT . f)