我应该避免使用Monad失败吗?
我对Haskell相当陌生,并且一直在慢慢地认识到Monad的存在有问题。真实世界哈斯克尔warns against its use(“再一次,我们建议你几乎总是避免使用失败!”)。我今天才注意到Ross Paterson把它称为“一个疣,而不是一种设计模式”back in 2008(并且似乎在该主题中得到了相当多的一致意见)。我应该避免使用Monad失败吗?
在看RalfLämmel博士talk on the essence of functional programming的同时,我开始明白可能导致Monad失败的紧张局势。在讲座中,拉尔夫谈到了向基本monadic解析器(日志记录,状态等)添加各种monadic效果。许多效果需要对基本解析器进行更改,有时还需要使用所使用的数据类型。我认为,向所有单子添加“失败”可能是一种妥协,因为“失败”如此常见,并且您希望尽可能避免更改“基本”解析器(或其他)。当然,某种'失败'对于解析器来说是有意义的,但并不总是,例如,放置/获取状态或者询问读者本地。
让我知道我是否可能走错了路。
我应该避免使用Monad失败吗? Monad失败的替代方法有哪些? 有没有其他monad库不包含这个“设计疣”? 我可以在哪里阅读关于这个设计决策历史的更多信息?
一些单子具有明显的失败机制,例如,终端单子:
data Fail x = Fail
一些单子没有合理的失效机理(undefined
不是明智的),例如最初的单子:
data Return x = Return x
在这个意义上,它显然要求所有的单子有fail
方法的疣。如果你正在编写通过单子进行抽象的程序(Monad m) =>
,那么使用这种通用的m
的fail
方法并不是很健康。这会导致一个函数,你可以使用monadad实例化,其中fail
应该不存在。
我在使用fail
(尤其是间接地,通过匹配Pat <- computation
)时发现,在明确指定了良好fail
行为的特定monad中工作时,反对意见较少。这样的计划希望能够在回归旧纪元的过程中生存下来,其中非平凡的模式匹配创造了MonadZero
而不仅仅是Monad
的需求。
有人可能会争辩说,更好的纪律总是明确地对待失败案例。我反对这个观点有两点:(1)单点编程的要点是为了避免这样的混乱,以及(2)目前用于单子计算结果的案例分析符号太糟糕了。 SHE的下一个版本将支持符号(也可在其他变体中找到)
case <- computation of
Pat_1 -> computation_1
...
Pat_n -> computation_n
这可能会有所帮助。
但这整个情况是一团糟。通过它们支持的操作来表征monad通常是有帮助的。您可以看到fail
,throw
等作为一些单子支持的操作,但不支持其他单元。 Haskell使得支持可用操作集中的小型本地化变更非常笨拙和昂贵,通过解释如何处理旧操作来引入新的操作。如果我们真的想在这里做一个更好的工作,我们需要重新思考如何工作,使其成为不同的本地错误处理机制之间的翻译。我经常想要在传递错误之前将一个计算失败(例如,通过模式匹配失败)来处理一个处理程序,该处理程序添加更多上下文信息。我不禁感到,有时候这样做比实际上要困难得多。
因此,这是一个可以做的更好的问题,但至少只能使用fail
来提供明智实现的特定单子,并正确处理“例外”。
不好意思,你提到的那个“SHE”是什么? –
这是Strathclyde Haskell Enhancement,我的预处理器支持实验性功能(主要是假相关类型)。但是现在,通过将这些功能的大部分应用到GHC中,SHE已经变得多余。 – pigworker
在Haskell 1.4(1997)中没有fail
。相反,有一个MonadZero
类型的类包含zero
方法。现在,do
表示法使用zero
来指示模式匹配失败;这给人们带来了惊喜:他们的功能是否需要Monad
或MonadZero
取决于他们如何在其中使用do
表示法。当Haskell 98被设计了一段时间后,他们做了一些改变,以使编程对新手更简单。例如,monad理解转化为列表解析。同样,要删除do
类型的问题,类MonadZero
已被删除;对于使用do
,方法fail
被添加到Monad
;对于zero
的其他用途,在MonadPlus
中增加了mzero
方法。
我认为这是一个很好的论据,fail
不应该明确地用于任何事情;其唯一预期用途是翻译do
表示法。不过,我自己也经常顽皮地使用fail
。
您可以访问原始1.4和98报告here。我确信导致改变的讨论可以在一些电子邮件清单档案中找到,但我没有方便的链接。
“我自己经常调皮,明确使用'fail''。经常?说明。 –
@丹伯顿:有什么可以解释的?如果我处于单一背景下,我倾向于使用“失败”来表示失败,除非我特别需要使用其他机制。 – ibid
谢谢。很高兴听到有关历史。我不知道Haskell最初只有标记(没有列表解析)。我在邮件列表存档http://www.mail-archive.com/[email protected]/msg03002.html –
我尽量避免Monad失败,并且根据您的情况有多种捕获失败的方法。 Edward Yang在他的博客中撰写了一篇名为8 ways to report errors in Haskell revisited的文章。
综上所述,不同的方式来报告错误,他确定是:
- 使用错误
- 使用也许
- 请使用字符串一个
- 使用了Monad并不能一概而论1- 3
- 使用MonadError和自定义错误类型
- 使用抛出IO单元
- 使用ioError在和catch
- 想怎么样用单子变压器
- 经过异常
- 失败
这些,我会受到诱惑,如果我知道如何处理错误使用选项3,用Either e b
,但需要更多的上下文。
或'也许'...使用任何一个都不错。尽管它使用一个使用Either的库和一个使用Maybe代码的库,但它很烦人。 Monad变形金刚不如... – alternative
[Real World Haskell says](http://book.realworldhaskell.org/read/monads.html#monads.monad.fail)“[in many monads]'fail' uses'error'。Calling'error'通常是非常不受欢迎的,因为它会抛出一个例外情况,呼叫者无法捕捉或不会期望。“ – Miikka
简答:是的。 –
我相信“失败”是需要处理模式匹配失败,当你像这样的东西:“只要x