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

时间:2011-11-17 08:05:02

标签: monads haskell

我是Haskell的新手,并且慢慢地认为Monad的存在失败了。真实世界Haskell warns against its use(“再一次,我们建议您几乎总是避免使用失败!”)。我今天才注意到,罗斯帕特森称之为“疣,而不是设计模式”back in 2008(似乎在那个帖子中得到了一些协议)。

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

让我知道我是否可能走错了路。

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

3 个答案:

答案 0 :(得分:27)

一些单子具有合理的失效机制,例如:终端monad:

data Fail x = Fail

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

data Return x = Return x

从这个意义上讲,要求所有monad都采用fail方法显然是一个瑕疵。如果您正在编写通过monad (Monad m) =>进行抽象的程序,那么使用该通用m的{​​{1}}方法并不是很健康。这将导致您可以使用monad实例化的函数,其中fail不应该存在。

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

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

Monad

这可能会有所帮助。

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

所以,这是一个可以做得更好的问题,但至少,只对提供合理实现的特定monad使用catch,并正确处理'例外'。

答案 1 :(得分: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报告here。我确信可以在某些电子邮件列表存档中找到导致更改的讨论,但我没有方便的链接。

答案 2 :(得分:6)

我试图尽可能避免Monad失败,并且根据您的情况,有多种方法可以捕获失败。 Edward Yang在题为8 ways to report errors in Haskell revisited的文章中对他的博客进行了很好的概述。

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

  1. 使用错误
  2. 使用Maybe a
  3. 使用字符串a
  4. 使用Monad并且无法概括1-3
  5. 使用MonadError和自定义错误类型
  6. 在IO monad中使用throw
  7. 使用ioError并捕获
  8. 坚持使用monad变形金刚
  9. 已检查的例外
  10. 失败
  11. 其中,如果我知道如何处理错误,我会很想使用选项3和Either e b,但需要更多的上下文。