单子vs箭


70

我对函数式编程中使用的monad箭头的概念非常熟悉。我也了解它们可以用来解决类似的问题。

但是,对于在任何给定情况下如何选择要使用的选项,我仍然有些困惑。

什么时候应该使用monad,什么时候应该使用箭头?


3
根据您发布的Wikipedia链接,单子是箭头的一种。
Michael Mior 2010年

Answers:


77

有通过林德利,Wadler和Yallop(在LTU讨论了两个优秀论文在这里)。

要了解的最重要的事情是,有更多的东西是比箭有东西是单子。相反,单子绝对比箭头强大(以上第二篇论文)精确指定了哪种方式)。

特别是,monad是带有type的apply函数的箭头(a ~> b, a) ~> b,其中(~>)给定箭头的构造函数。Lindley等。指出这破坏了术语和命令(或者,如果您喜欢,对象和词素)之间保持的细致区分。

应用函子具有广泛的应用,特别是对于那些最好考虑作为流操作的事物。实际上,人们可以认为箭头是由于泛化了转换器在流上的概念而引起的(即,为由给定的应用函数构造的对象引入了一种新的态射语言)。

根据我的经验,由于monad使对象和词素之间的区别模糊(即,如果我正确使用了单词,会产生封闭的笛卡尔类别),那么monad中的术语通常比in中的术语更不透明。箭头或适用函子(尽管请注意,两者都可以分别通过arrpure方法注入任意函数)。

因此,如果某些东西没有被赋予单声道的特征(即使从概念上说它构成一个),那么它就有可能接受更大的检查和优化。序列化也可能更容易。因此,在解析器和电路建模中使用了应用程序和箭头。


以上试图是概括性的和描述性的。以下是一些我自以为是的经验法则。

如果您需要建模看起来像状态的东西,请从monad开始。如果您需要建模看起来像全局控制流的东西(例如,异常,延续),请从monad开始。如果出现与monad的功能和通用性冲突的要求(即,对于它们而言,join的(join :: m (m a) -> m a)功能太强大),则可以考虑减少所使用事物的功能。

如果您需要对流进行建模,并对流进行转换,尤其是对某些特性(尤其是过去和将来的无限视图)应该是不透明的流进行建模,则可以从应用函子开始。如果您需要有关流上转换属性的更强论据,请考虑使用箭头。

或者,简而言之,应用程序用于电路的动作,箭头用于电路的结构,单子用于通用计算效果。

故事当然还有很多。有关应用程序,请参阅Conal Elliott关于FRP的工作。有关箭头,请参阅HXT XML解析器库Yampa FRP项目Horkell on Horse网络框架,Hudak和Liu的经典著作《用箭头插入空间泄漏》,以及其他内容。对于单子,无处不在。当然要注意的是,仅仅因为某物是单子,并不意味着应用的符号可能不会更清晰和更具表现力。


谢谢-非常有用,很高兴听到您的观点!
mikera 2010年

2
由于信息溢出,我投了反对票。答案产生的术语多于明确的指导,使我更加茫然和害怕箭头。Dimitrios的直接回答比这更清晰的指导(没有冒犯!)
user5193682

@ user5193682答案是完全错误的
尼斯,

15

简短的答案是,“箭”比“单体”更通用,使用起来也比较麻烦。因此,您应尽可能使用Monad,而在Monad不适用的情况下则使用Arrows。

接下来是“风景路线”答案。

介绍Arrows的人John Hughes发表了两篇我推荐的出色论文:“将monads泛化为Arrows”“ Programming with Arrows”。这两篇文章都很容易阅读,并为您的问题提供了答案。即使有些人不了解这两篇文章中的所有细节或代码,他们当然也会找到很多有关Monads和Arrows的信息和非常有用的解释。

我现在将重点介绍这些文章中与您的问题有关的要点

当引入Monad时,人们认为它们无所不能。确实,Monads具有很多功能。但在某些时候,发现有些情况下无法应用单声道。这些情况与多个输入有关,尤其是当某些输入是静态的而某些输入是动态的时。因此,约翰·休斯(John Hughes)站出来介绍了箭矢(Arrows)。

箭比单子更通用。箭头是Monad的超集。他们可以做Monads可以做的所有事情,甚至更多。但是它们也更难以使用。约翰·休斯(John Hughes)建议您应尽可能使用Monad,而不能使用Monad则应使用Arrows。

我同意约翰·休斯的观点。我也想起了爱因斯坦的名言:“一切都应该变得尽可能简单,而不是简单”。

当然,这完全取决于当前的特定情况。让我解释。让我们假设您正在学习Haskell。然后,使用monadic方法执行每个程序并使用基于箭头的方法重做每个程序将是一项艰巨的任务。当您学习时,您应该努力探索所有可能性并实施各种方法。这样,您可以获得深刻的见解,并且能够直接比较不同的解决方案。

现在,让我们假设您想为社区提供一个图书馆。好吧,这归功于将阅读代码的人员,您应该使用最容易理解的方法,并且仍然可以完成工作。您还应该感谢将使用您的代码的人员,因为您的解决方案没有不必要的复杂性。这样,您的解决方案将更易于维护,并且不易出现错误和错误。

但是,如果您处于临界状态,该怎么办?让我们假设您不确定您是否需要箭头的额外功能。那你该怎么办 您是否应该从一元方法开始,然后在需要时改用基于箭头的方法?还是应该从一开始就使用Arrows,避免在项目进行到一半时进行昂贵的转换?

同样,我的答案是尝试第一种方法:如果可以,尝试使用Monads。如果以后发现不能使用Monads,则必须忍受昂贵的切换,必须重新启动并重做项目才能使用Arrows。这种方法肯定会需要您花费更多的时间和其他资源。但是您会知道您做对了,这是为了尝试提供最简单,最清晰,最简单的解决方案。

避免不必要的复杂性是最重要的。信不信由你,这就是将类别理论中的概念(例如函数组成,Monads和Arrows)引入计算机科学的原因。具有讽刺意味?


4
“箭头比Monads更通用。箭头是Monads的超集。它们可以完成Monads的所有工作,甚至更多。”请问您对第三句话的意思是什么?该句子似乎与前两个矛盾。函子是Monad的超集,恕我直言,它们可以做的比Monad少(即仅fmap)。
jhegedus

我明白你为什么感到困惑。问题是我使用的单词“一般”和术语“超集”相当宽松。因此,为了使我理解,我声明:函子的作用小于Monads的作用。Monad的作用比Arrows的作用小。结果,Functors的功能最弱,Monads的功能更大,而Arrows的功能最大。在这里,“权力”一词的意思是“做事的能力,功能”。
Dimitrios Kalemis

感谢Dimitrios的澄清。
jhegedus

3
“ Monad的作用比Arrows的作用要小” –这是完全错误的,Monads比Arrows强大,这就是为什么Arrows并非Monads的原因。
Matthias Berndt '18年

3
我在这里同意马蒂亚斯的观点。每个monad都是函子,但并非每个Arrow都是monad。您需要ArrowApply从箭头获得单子。另一方面,可以通过Kleisli
WorldSEnder '18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.