我应该避免使用Monad失败吗? [英] Should I avoid using Monad fail?

查看:102
本文介绍了我应该避免使用Monad失败吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Haskell相当陌生,并且一直在慢慢地认识到Monad的存在出现问题。真实世界Haskell 警告不要使用它(我们再次推荐你几乎总是避免使用失败!)。我今天才注意到,Ross Paterson称它为一个疣,而不是一种设计模式早在2008年(似乎在该主题中得到了相当一致的结果)。



在观看RalfLämmel博士时

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



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

解决方案

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

 数据失败x =失败

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

  data返回x =返回x 

从这个意义上说,显然要求所有单子有一个失败方法。如果你正在编写一个通过monad (Monad m)=> 抽象的程序,那么使用这个通用的 m 失败方法。这将导致你可以用monad实例化一个函数,其中失败应该不存在。



我看到使用失败(特别是间接地通过匹配 Pat < - computation )时,反对使用特定monad时更少已经明确指出了一个很好的失败行为。这样的程序希望能够在恢复到原来的纪律之后继续存在,其中非平凡模式匹配创建了 MonadZero 而不是 Monad

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

  case<  - 计算
Pat_1 - > comput_1
...
Pat_n - > comput_n

这可能会有所帮助。



但是这整个情况是一团糟。通过它们支持的操作来表征monad通常是有帮助的。你可以看到失败 throw 等操作被一些monad支持,但不是其他的。 Haskell使得支持可用操作集中的小型本地化变更非常笨拙和昂贵,通过解释如何处理旧操作来引入新的操作。如果我们真的想在这里做一个更好的工作,我们需要重新思考 catch 是如何工作的,使它成为不同的本地错误处理机制。我经常想要在传递错误之前将一个计算失败(例如,通过模式匹配失败)来处理一个处理程序,该处理程序添加更多上下文信息。我不禁感到有时候做起来会比应该更困难。因此,这是一个可以做得更好的问题,但在非常至少,对于提供合理实现的特定单子,只能使用失败,并正确处理'例外'。


I'm fairly new to Haskell and have been slowly getting the idea that there's something wrong with the existence of Monad fail. Real World Haskell warns against its use ("Once again, we recommend that you almost always avoid using fail!"). I just noticed today that Ross Paterson called it "a wart, not a design pattern" back in 2008 (and seemed to get quite some agreement in that thread).

While watching Dr Ralf Lämmel talk on the essence of functional programming, I started to understand a possible tension which may have led to Monad fail. In the lecture, Ralf talks about adding various monadic effects to a base monadic parser (logging, state etc). Many of the effects required changes to the base parser and sometimes the data types used. I figured that the addition of 'fail' to all monads might have been a compromise because 'fail' is so common and you want to avoid changes to the 'base' parser (or whatever) as much as possible. Of course, some kind of 'fail' makes sense for parsers but not always, say, put/get of State or ask/local of Reader.

Let me know if I could be off onto the wrong track.

Should I avoid using Monad fail? What are the alternatives to Monad fail? Are there any alternative monad libraries that do not include this "design wart"? Where can I read more about the history around this design decision?

解决方案

Some monads have a sensible failure mechanism, e.g. the terminal monad:

data Fail x = Fail

Some monads don't have a sensible failure mechanism (undefined is not sensible), e.g. the initial monad:

data Return x = Return x

In that sense, it's clearly a wart to require all monads to have a fail method. If you're writing programs that abstract over monads (Monad m) =>, it's not very healthy to make use of that generic m's fail method. That would result in a function you can instantiate with a monad where fail shouldn't really exist.

I see fewer objections to using fail (especially indirectly, by matching Pat <- computation) when working in a specific monad for which a good fail behaviour has been clearly specified. Such programs would hopefully survive a return to the old discipline where nontrivial pattern matching created a demand for MonadZero instead of just Monad.

One might argue that the better discipline is always to treat failure-cases explicitly. I object to this position on two counts: (1) that the point of monadic programming is to avoid such clutter, and (2) that the current notation for case analysis on the result of a monadic computation is so awful. The next release of SHE will support the notation (also found in other variants)

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

which might help a little.

But this whole situation is a sorry mess. It's often helpful to characterize monads by the operations which they support. You can see fail, throw, etc as operations supported by some monads but not others. Haskell makes it quite clumsy and expensive to support small localized changes in the set of operations available, introducing new operations by explaining how to handle them in terms of the old ones. If we seriously want to do a neater job here, we need to rethink how catch works, to make it a translator between different local error-handling mechanisms. I often want to bracket a computation which can fail uninformatively (e.g. by pattern match failure) with a handler that adds more contextual information before passing on the error. I can't help feeling that it's sometimes more difficult to do that than it should be.

So, this is a could-do-better issue, but at the very least, use fail only for specific monads which offer a sensible implementation, and handle the 'exceptions' properly.

这篇关于我应该避免使用Monad失败吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆