如何使用没有 SomeException 作为其参数类型的处理程序

hgi*_*sel 2 haskell exception-handling

在使用Control.Monad.Throw(即exceptions包)时,让我感到困惑的是,似乎我所有的投掷和接球都必须使用SomeException.

例如

value :: Either ExitCode String
value = throwM $ ExitFailure 23
Run Code Online (Sandbox Code Playgroud)

这在我看来应该编译,因为throwM :: (Exception e, MonadThrow m) => e -> m a, 并且ExitCode有一个 Exception 实例。即使这样也不能编译:

value :: Exception e => Either e String
value = throwM $ ExitFailure 23
Run Code Online (Sandbox Code Playgroud)

实际上,它仅在我将签名更改为SomeException. 我知道Exception类型类有一个特殊的地方

Control.Exception文档中,我可以看到它们catches与签名ArithException -> m a或类似处理程序一起使用的示例。我测试了它并且它起作用了。

使用时这是不可能的exceptions吗?

编辑错误消息是:

无法匹配类型ExitCode与使用SomeException引起的throwM

或者

无法匹配类型e与使用SomeException引起的throwM

Jus*_* L. 5

您看到的行为来自以下类型的签名throwM

throwM :: (Exception e, MonadThrow m) => e -> m a
Run Code Online (Sandbox Code Playgroud)

和 的实例Either,本质上是:

MonadThrow (Either SomeException)
Run Code Online (Sandbox Code Playgroud)

这使得throwM

throwM :: (Exception e) => e -> Either SomeException a
Run Code Online (Sandbox Code Playgroud)

throwM因为Either SomeException可以带任何Exception e

但是,没有 MonadThrow 实例for Either ExitCode, 或 for forall e. Exception e => Either e

问题是没有真正的方法可以编写一个对所有e. 想象一下有一个实例

Exception q => MonadThrow (Either q)
Run Code Online (Sandbox Code Playgroud)

这将使throwM

throwM :: (Exception e, Exception q) => e -> Either q a
Run Code Online (Sandbox Code Playgroud)

这意味着您必须能够将any e转换为any q,而单独使用类型类是不可能Exception

还设想一下,如果有一个MonadThrow实例Either ExitCode。这将使类型签名为throwM

throwM :: Exeption e => e -> Either ExitCode a
Run Code Online (Sandbox Code Playgroud)

您可能会看到这显然是荒谬的,因为有许多实例Exception不能强制转换为ExitCode. (如果您不相信我,请尝试使用该类型签名编写一个函数!)

如果您只想要 的短路异常行为Either,请考虑:

  1. 只需使用Left代替throwM, 和模式匹配即可catch。如果您真的想使用Exceptionstill,您可以使用fromException来尝试将 a 强制SomeException转换Exception为您选择的实例。
  2. 您可以使用exceptions包,只要您必须假设异常可能是任何实例 ( SomeException) ,捕获仍然有效
  3. 使用MonadErrorExceptTfrom mtl,如果您想要某种具有捕获能力的多态界面