Control.Exception.Safe,为什么ExceptT和Either的表现如此不同?

时间:2017-12-29 22:31:15

标签: haskell exception error-handling either

我尝试将Control.Exception.SafeControl.Monad.Except一起使用。

throwString "Foo" :: Except String a
-- error, no instance for `MonadTrow Identity`

好的,显然Except将其错误抛出到变压器堆栈中的底层monad中? 但那是为什么呢? Isn&#t; t Except基本上是为处理异常而设计的吗?为什么这种奇怪的行为?为什么不等同于Left "Foo"

编辑:

好的进一步说明我的问题:

我认为ExceptT e m aEither e a ReaderT a m ba -> b的所有内容。 Control.Monad.Except.throwErrorcatchErrorControl.Exception.Safe.throw完全相同,catchEither e a完全相同。 但是,当应用于Except e a时,它们会突然发挥作用。

当我想使用由Either e a提供的Control.Exception.Safe但在monad变换器环境中的行为时,该怎么办?

我的背景是我做了#34;在48小时内为自己写一个方案"并且想要推广错误(使用MonadThrow),这样我就可以用它做一些IO。

EDIT2:

示例:

data CountError = CountError deriving (Show, Except)

x :: String -> ExceptT CountError (Writer [String]) Int
x str = do { lift $ writer (length "str", return str); }

现在这会计算字符并收集编写器中的字符串。这可以扩展你想要的多少。错误可能表示"字符串"中的错误字符,或"太多字符"或其他。

y :: MonadThrow m => m a
y = throw CountError

这是一个非常普遍的例外,除了ExceptT之外,我可以将它用于任何其他类型的组合:

y >> x
-- No instance for MonadThrow Identity
-- But what I want is (Left CountError, [])

2 个答案:

答案 0 :(得分:1)

  

好的,显然Except将其错误抛出到变压器堆栈中的底层monad中?

正如我在评论中所说:除非“将它的错误抛给底层monad”,这正是MonadThrow实例正在做的事情。

  

但为什么会这样?

MonadThrow的实例无法为所有类型ExceptT ee抛出一个String异常,而不是仅为ExceptT String创建一个实例,作者似乎解除了异常下一个更高的monad。

  

是否除了基本上设计用于处理异常?

确实Except旨在允许在特殊情况下失败。

迂腐,我称之为例外的替代品。 monad管道另一种返回概念(错误情况)并自称“Except”并不意味着任何典型的异常选项,例如低级别堆栈展开,都在使用中。

  

为什么这种奇怪的行为?

由于MonadThrow实例,从Control.Monad.Throw重新导出:

-- | Throws exceptions into the base monad.
instance MonadThrow m => MonadThrow (ExceptT e m) where
  throwM = lift . throwM
  

为什么不等同于左“Foo”

因为那时实例必须是ExceptT String而不是ExceptT e。或者,这就是为什么我认为exceptions(Edward Kmett)的作者决定了这个设计。

相反,请考虑使用Control.Monad.Except.throwError来执行您想要的操作。

  

当我想使用由Control.Exception.Safe提供但在monad变换器的上下文中的Either e a行为时,该怎么办?

你在说什么“Either e a的行为?你在寻找什么不同于throwError?据我所知,你正在寻找一个不必要的额外抽象层。

答案 1 :(得分:1)

这是一个老问题了,但是随着我最近在工作中遇到这个话题,我认为这个答案还有改进的余地。让我们看看,我相信您对ExceptT的行为与Either类似的期望是有道理的,但是不幸的是MonadThrow的作者选择了不同的立场,并决定“通过”关于如何向基础单子(或变压器)“抛出”故障是正确的方法。在不试图推测为什么要做出决定的情况下,我同意这种行为违背了使用诸如“ MaybeT”或“ ExceptT”之类的“故障”变换器作为错误类(例如MonadThrow)的目的,即将“ Maybe”或“ Either”之类的语义赋予另一个monad。

由于这个原因,我穿上了火焰背心,在Control.Monad.Failable刺了一下,这完全符合您(和我)的期望,即返回Left e,其中e是Exception的实例。

Control.Monad.Failable