如何使用SomeException作为参数类型

时间:2018-03-04 21:14:35

标签: haskell exception-handling

使用Control.Monad.Throw(即exceptions包)时,让我感到困惑的是,我的所有投掷和捕捉都必须与SomeException一起使用。

E.g。

value :: Either ExitCode String
value = throwM $ ExitFailure 23

这让我觉得它应该编译,因为throwM :: (Exception e, MonadThrow m) => e -> m aExitCode有一个Exception实例。即使这样也无法编译:

value :: Exception e => Either e String
value = throwM $ ExitFailure 23

实际上,只有在我将签名更改为SomeException时才会编译。我知道Exception类型类有一个特殊的地方重新

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

使用exceptions时无法实现这一点吗?

EDIT 错误消息是:

  由于使用ExitCode

而导致SomeExceptionthrowM类型无法匹配

  由于使用e

而导致SomeExceptionthrowM类型无法匹配

1 个答案:

答案 0 :(得分:4)

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

throwM :: (Exception e, MonadThrow m) => e -> m a

Either的实例,基本上是:

MonadThrow (Either SomeException)

这使throwM

throwM :: (Exception e) => e -> Either SomeException a
throwM的{​​{1}}可以使用任何Either SomeException

但是, Exception eEither ExitCode没有MonadThrow实例

问题在于,没有办法为所有forall e. Exception e => Either e编写一个多态的实例。想象一下有一个实例

e

这会使Exception q => MonadThrow (Either q)

throwM

这意味着你必须能够任何 throwM :: (Exception e, Exception q) => e -> Either q a 并将其转换为任何 e是不可能的仅使用q类型类。

想象一下,Exception是否有MonadThrow个实例。这将使Either ExitCode的类型签名:

throwM
您可能看到的

显然是荒谬的,因为throwM :: Exeption e => e -> Either ExitCode a 的许多实例无法强制转换为Exception。 (如果您不相信我,请尝试编写具有该类型签名的函数!)

如果您只想 ExitCode的短暂异常行为,请考虑:

  1. 只需使用Either代替Left,并使用throwM的模式匹配。如果确实仍想使用catch,您可以使用Exception尝试将fromException强制转换为您选择的SomeException个实例。
  2. 您可以使用例外包,只要您必须假定异常可以是任何实例(Exception
  3. ,捕获仍然有效
  4. 如果您想要某种具有捕获能力的多态接口,请使用 mtl 中的SomeExceptionMonadError