我应该避免使用Monad失败吗?

Ste*_*haw 34 monads haskell

我对Haskell相当新,并且慢慢地认为Monad的存在有问题.真实世界Haskell 警告不要使用它("再一次,我们建议你几乎总是避免使用失败!").我今天只注意到罗斯帕特森称之为"疣,而不是设计模式" 在2008年后(并似乎得到该线程颇有些协议).

在观看RalfLämmel博士谈论函数式编程的本质时,我开始理解可能导致Monad失败的紧张局势.在讲座中,Ralf讨论了向基础monadic解析器添加各种monadic效果(日志记录,状态等).许多效果需要更改基本解析器,有时还需要更改使用的数据类型.我认为向所有monad添加'fail'可能是一种妥协,因为'fail'是如此常见,你想尽可能避免更改'base'解析器(或其他).当然,某种"失败"对于解析器来说是有意义的,但并不总是,例如,put/get of State或者ask/local of Reader.

如果我可能走错了轨道,请告诉我.

我应该避免使用Monad失败吗?Monad失败的替代方案是什么?是否有任何替代monad库不包括这个"设计疣"?我在哪里可以阅读有关此设计决策的历史更多信息?

pig*_*ker 27

一些monad有一个明智的失败机制,例如终端monad:

data Fail x = Fail
Run Code Online (Sandbox Code Playgroud)

有些monad没有明智的失败机制(undefined不合理),例如最初的monad:

data Return x = Return x
Run Code Online (Sandbox Code Playgroud)

从这个意义上说,要求所有monad都有一个fail方法显然是一个瑕疵.如果你正在编写抽象monad的程序(Monad m) =>,那么使用该泛型mfail方法并不是很健康.这将导致您可以使用monad实例化的函数,其中fail不应该存在.

我看到在使用明确指定了良好行为的特定monad中使用fail(尤其是间接匹配Pat <- computation)的反对意见较少fail.这些程序有望在返回旧规则后继续存在,在这种规则中,非平凡的模式匹配产生了对MonadZero而不是仅仅的需求Monad.

有人可能会说,更好的纪律总是明确地处理失败案件.我反对这个立场有两个方面:(1)monadic编程的目的是避免这种混乱,(2)对于monadic计算结果的案例分析的当前符号是如此糟糕.SHE的下一个版本将支持该符号(也可在其他变体中找到)

case <- computation of
  Pat_1 -> computation_1
  ...
  Pat_n -> computation_n
Run Code Online (Sandbox Code Playgroud)

这可能会有所帮助.

但这整个局面令人遗憾.通过它们支持的操作来表征monad通常是有帮助的.您可以看到fail,throw等等作为某些monad支持的操作,但不支持其他monad.Haskell使得支持可用操作集中的小型本地化更改非常笨拙和昂贵,通过解释如何根据旧操作处理它们来引入新操作.如果我们真的想在这里做一个更整洁的工作,我们需要重新思考如何catch工作,使其成为不同本地错误处理机制之间的翻译.我经常想要计算一个计算,该计算可能会失去信息(例如,通过模式匹配失败)与处理程序,该处理程序在传递错误之前添加更多上下文信息.我不禁觉得这样做有时候比应该做的更难.

因此,这是一个可以做得更好的问题,但至少fail只能用于提供合理实现的特定monad,并正确处理"异常".

  • 这是 Strathclyde Haskell Enhancement,我的预处理器支持实验功能(主要是假依赖类型)。但是现在,由于 GHC 中采用了大部分功能,SHE 变得多余了。 (3认同)

ibi*_*bid 25

在Haskell 1.4(1997)中没有fail.相反,有一个MonadZero包含zero方法的类型类.现在,do用于zero表示模式匹配失败的符号; 这给人们带来了惊喜:他们的功能是否需要MonadMonadZero依赖于他们如何使用其中的do符号.

当Haskell 98稍后设计时,他们做了几处修改,使编程对新手来说更简单.例如,monad理解被转化为列表理解.同样,要删除do类类问题,MonadZero该类已被删除; 为了使用do,该方法fail被添加到Monad; 对于其他用途zero,mzero增加了一种方法MonadPlus.

我认为,有一个很好的论据,fail不应该明确地用于任何事情; 它唯一的用途是用于do表示法的翻译.尽管如此,我自己也经常调皮并fail明确使用.

您可以在此处访问原始的1.4和98报告.我确信导致更改的讨论可以在某些电子邮件列表存档中找到,但我没有方便的链接.

  • @DanBurton:有什么可以解释的?如果我处于monadic环境中,我倾向于使用`fail`来表示失败,除非我特别需要使用其他机制. (6认同)

Nic*_* Wu 6

我试图尽可能地避免Monad失败,并且根据您的情况,有多种方法可以捕获失败.Edward Yang在他的博客上写了一篇很好的概述,题为" 报告Haskell错误的8种方法".

总之,报告错误的不同方法是:

  1. 使用错误
  2. 使用Maybe a
  3. 使用字符串a
  4. 使用Monad并且不能概括1-3
  5. 使用MonadError和自定义错误类型
  6. 使用抛出IO monad
  7. 使用ioError并捕获
  8. 与monad变形金刚一起疯狂
  9. 检查异常
  10. 失败

其中,我很想使用选项3,Either e b如果我知道如何处理错误,但需要更多的上下文.