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

我对Haskell相当陌生,并且一直在慢慢认识到Monad的存在出现问题。 真实世界Haskell警告不要使用它(“再一次,我们建议你几乎总是避免使用失败!”)。 我今天才注意到,罗斯帕特森在2008年把它称为“一种疣,而不是一种设计模式”(并且似乎在该主题中达成了一些共识)。

在观看RalfLämmel博士谈论函数式编程的本质时,我开始理解可能导致Monad失败的可能的紧张。 在讲座中,拉尔夫谈到了向基本monadic解析器(日志记录,状态等)添加各种monadic效果。 许多效果需要对基本解析器进行更改,有时还需要使用所使用的数据类型。 我认为,向所有单子添加“失败”可能是一种妥协,因为“失败”如此常见,并且您希望尽可能避免更改“基本”解析器(或其他)。 当然,某种“失败”对于解析器来说是有意义的,但并不总是,比如说,放置/获取状态,或者询问读者本地。

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

我应该避免使用Monad失败吗? Monad有哪些替代方法会失败? 有没有其他monad库不包含这个“设计疣”? 我可以在哪里阅读关于这个设计决策历史的更多信息?


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

data Fail x = Fail

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

data Return x = Return x

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

在特定monad中工作时,我发现对使用fail的反对意见更少(特别是间接地,通过匹配Pat <- computation ),并且明确指定了良好的fail行为。 这样的程序希望能够在恢复到原来的纪律之后继续存在,在这种纪律下,非平凡的模式匹配创造了MonadZero而不是Monad的需求。

有人可能会争辩说,更好的纪律总是明确地对待失败案例。 我反对这个观点有两点:(1)单点编程的要点是为了避免这样的混乱,以及(2)目前用于单子计算结果的案例分析符号太糟糕了。 SHE的下一个版本将支持符号(也可以在其他变体中找到)

case <- computation of
  Pat_1 -> computation_1
  ...
  Pat_n -> computation_n

这可能会有所帮助。

但是这整个情况是一团糟。 通过它们支持的操作来表征monad通常是有帮助的。 你可以看到一些单子支持的操作failthrow等,但不是其他单元。 Haskell使得支持可用操作集中的小型本地化变更非常笨拙和昂贵,通过解释如何处理旧操作来引入新的操作。 如果我们真的想在这里做一个更好的工作,我们需要重新思考catch如何工作,使它成为不同本地错误处理机制之间的翻译者。 我经常想要将一个计算失败(例如,通过模式匹配失败)来处理可能失败的计算,并在传递错误之前添加更多上下文信息。 我不禁感到,有时候这样做比实际上要困难得多。

所以,这是一个可能-做更好的问题,但最起码,使用fail只为它提供一个合理的实现特定的单子,并办理“例外”正确。


在Haskell 1.4(1997)中,并没有fail 。 相反,有一个包含zero方法的MonadZero类型类。 现在, do记号用zero来表示模式匹配失败; 这引起了惊喜的人:他们的功能是否需要MonadMonadZero取决于他们如何使用do记号在里面。

当Haskell 98稍后设计时,他们做了一些改变,以使编程对新手更简单。 例如,monad理解转化为列表解析。 同样,为了消除do类的问题, MonadZero类被删除; 为了使用do ,该方法fail被添加到Monad ; 和用于其它用途zero ,一mzero方法加入到MonadPlus

我认为,应该提出一个很好的论点,即fail应该明确地使用任何fail ; 其唯一的用途是在翻译do记号。 尽管如此,我自己也经常顽皮而且使用fail

您可以在这里访问原始的1.4和98报告。 我确信导致改变的讨论可以在一些电子邮件清单档案中找到,但我没有方便的链接。


我尽量避免Monad失败,并根据您的情况采取各种方式来捕获失败。 Edward Yang在他的博客中撰写了一篇很好的综述,文章名为“重新编写Haskell中的错误报告的8种方法”。

总之,报告错误的方法有以下几种:

  • 使用错误
  • 使用也许a
  • 使用任一字符串a
  • 使用Monad并且不能概括1-3
  • 使用MonadError和自定义错误类型
  • 使用抛出IO monad
  • 使用ioError并捕获
  • 坚持单身变压器
  • 检查异常
  • 失败
  • 其中,我会尝试使用选项3,用Either eb如果我知道如何处理错误,但需要多一点的环境。

    链接地址: http://www.djcxy.com/p/43241.html

    上一篇: Should I avoid using Monad fail?

    下一篇: Comparison of Priority Queue implementations in Haskell