"除了"的复杂性在Haskell中的作用是什么?

oro*_*ome 4 error-handling monads haskell exception-handling monad-transformers

理解(我认为)之间存在密切的关系Either,并Except在Haskell,而且它很容易从一个转换到另一个.但是我对在Haskell中处理错误的最佳实践以及在什么情况和情况下我会选择其中一个而感到困惑.例如,在提供的示例Control.Monad.Except,Either用于定义

type LengthMonad = Either LengthError
Run Code Online (Sandbox Code Playgroud)

calculateLength "abc"就是

Right 3
Run Code Online (Sandbox Code Playgroud)

相反,如果要定义

type LengthMonad = Except LengthError
Run Code Online (Sandbox Code Playgroud)

calculateLength "abc"就是

ExceptT (Identity (Right 3))
Run Code Online (Sandbox Code Playgroud)

我很困惑这会起什么作用,何时需要它.为什么从返回的一切calculateLength总是有Identity; 为什么不只是SomeExceptionType (Right 3)甚至SomeSuccessType 3

当谈到像这样的概念时,我是一个Haskell的初学者,所以当我希望后者超过前者时,我会非常感激,特别是为什么它(显然对我来说)很复杂.例如,使用do Except版本的函数的调用者calculateLength,他们不能(或至少不能轻易地)使用该Either版本的内容是什么?

Mar*_*ann 5

抽象

使用Either正常成功/错误的API.它在基础库中定义,因此不会将其他依赖项推送到使用者身上.此外,这是最基本的Haskell类型之一,因此'每个人'都了解它的工作原理.

ExceptT在您特别需要Either与另一个monad(例如IO)组合时才使用.此类型在变换器库中定义,因此会对消费者产生额外的依赖性.另外,monad变换器是Haskell的一个更高级的功能,所以你不能指望每个人都能理解如何使用它.

对原因的猜测

做出这些决定时,我并不在身边,但似乎有各种历史原因引起混淆.Haskell是一种古老的语言(比Java早!),所以尽管已经努力简化它并纠正旧的错误,但仍有一些仍然存在.据我所知,Either/ ExceptT混乱是其中一种情况.

猜测Either比monad变换器的概念要老,所以我想这个类型Either是在Haskell历史早期引入基础库的.

似乎也是如此Maybe.

其他monad,例如ReaderState似乎已经与他们的monad变换器一起被引入(或至少'retconned').例如,Reader仅仅是一个特例ReaderT,这里的"其他" MonadIdentity:

type Reader r = ReaderT r Identity
Run Code Online (Sandbox Code Playgroud)

同样适用于StateT:

type State s = StateT s Identity
Run Code Online (Sandbox Code Playgroud)

这是变形金刚库中定义的许多monad的一般模式.ExceptT通过定义Except为特殊情况来跟随模式ExceptT.

这种模式有例外.例如,MaybeT没有定义Maybe为特例.我再次相信这是出于历史原因; Maybe可能在很久之前,有人开始研究变形金刚图书馆.

这个故事Either似乎更加复杂.据我所知,还有就是,原来,一个EitherT单子转换,但显然(我忘了细节)有一些错误的方式,它的表现(它可能打破了一些法律),因此,它与另一个叫变压器更换ErrorT,这再次证明是错误的.我想,第三次是魅力所以ExceptT被介绍了.

Control.Monad.Trans.Except模块遵循大多数其他monad变换器的模式,通过使用类型别名定义"无效"特殊情况:

type Except e = ExceptT e Identity
Run Code Online (Sandbox Code Playgroud)

我想这样做是因为它可以,但它可能是不幸的,因为它令人困惑.绝对现有技术表明monad变换器不必遵循该模式(例如MaybeT),所以我认为如果模块没有这样做会更好,但确实如此,那就是我们所处的位置.

我基本上会忽略Except类型和使用Either,但ExceptT如果需要变压器则使用.

  • [2/2] 该选择的支持者认为,“Either”应该是一个中性的和类型,不应与异常语义相关联。这与经常针对元组的“函子”和朋友的实例提出的论点类似。有关参数的详细信息,请参阅 [库邮件列表中的此线程](https://mail.haskell.org/pipermail/libraries/2013-August/020565.html)。就我个人而言,我认为使用 `ExceptT` 而不是 `EitherT` 只会造成可避免的混淆,正如在这个问题中所见。 (3认同)
  • @orome “Identity” monad 是一个无操作 monad,不会添加额外的“效果”。Haskell 与数学(特别是范畴论)存在联系,因此如果数学中存在某种“事物”,您也可以经常在 Haskell 中找到它,即使乍一看它的价值有限。“身份”很适合这个类别。乍一看,它如何增加任何价值并不明显,但它可以用来将一元 API 转变为没有效果的 API,以防万一您需要。 (2认同)