使用Control.Monad.Throw
(即exceptions
包)时,让我感到困惑的是,我的所有投掷和捕捉都必须与SomeException
一起使用。
E.g。
value :: Either ExitCode String
value = throwM $ ExitFailure 23
这让我觉得它应该编译,因为throwM :: (Exception e, MonadThrow m) => e -> m a
和ExitCode
有一个Exception实例。即使这样也无法编译:
value :: Exception e => Either e String
value = throwM $ ExitFailure 23
实际上,只有在我将签名更改为SomeException
时才会编译。我知道Exception
类型类有一个特殊的地方重新
在documentation for Control.Exception中,我可以看到他们将catches
与签名ArithException -> m a
或类似处理程序一起使用的示例。我测试了它并且它起作用了。
使用exceptions
时无法实现这一点吗?
EDIT 错误消息是:
由于使用ExitCode
而导致SomeException
与throwM
类型无法匹配
或
由于使用e
而导致SomeException
与throwM
类型无法匹配
答案 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 e
或Either 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
的短暂异常行为,请考虑:
Either
代替Left
,并使用throwM
的模式匹配。如果确实仍想使用catch
,您可以使用Exception
尝试将fromException
强制转换为您选择的SomeException
个实例。 Exception
)SomeException
和MonadError