我尝试将Control.Exception.Safe
与Control.Monad.Except
一起使用。
throwString "Foo" :: Except String a
-- error, no instance for `MonadTrow Identity`
好的,显然Except
将其错误抛出到变压器堆栈中的底层monad中?
但那是为什么呢? Isn&#t; t Except
基本上是为处理异常而设计的吗?为什么这种奇怪的行为?为什么不等同于Left "Foo"
编辑:
好的进一步说明我的问题:
我认为ExceptT e m a
是Either e a
ReaderT a m b
到a -> b
的所有内容。 Control.Monad.Except.throwError
和catchError
与Control.Exception.Safe.throw
完全相同,catch
与Either 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, [])
答案 0 :(得分:1)
好的,显然Except将其错误抛出到变压器堆栈中的底层monad中?
正如我在评论中所说:除非“将它的错误抛给底层monad”,这正是MonadThrow实例正在做的事情。
但为什么会这样?
MonadThrow的实例无法为所有类型ExceptT e
向e
抛出一个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
的实例。