Monads vs. Arrows

时间:2010-09-06 14:00:19

标签: language-agnostic haskell functional-programming monads arrows

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

但是,我仍然对如何选择在任何特定情况下使用哪一个感到困惑。

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

2 个答案:

答案 0 :(得分:71)

Lindley,Wadler& Co. Yallop(在LTU here讨论)。

最重要的是要理解的是,更多的东西是箭头而不是monad。相反,monad比箭头更强大(second paper above精确地指定了哪种方式)。

特别是,monad是带有(a ~> b, a) ~> b类型的apply函数的箭头,其中(~>)是给定箭头的构造函数。林德利等人。指出这会破坏术语和命令之间保持的细致区分(或者,如果你愿意,还会破坏对象和态射)。

Applicative仿函数具有广泛的应用程序,特别是对于最好被认为是对流的操作的东西。事实上,人们可以将箭头视为在流上推广变换器概念而产生的(即在由给定的应用仿函数构造的对象上引入态射的新语言)。

根据我的经验,因为monad模糊了对象和态射之间的区别(即,如果我正确地使用了单词,产生了一个封闭的笛卡尔类别),那么monad中的术语通常不一定是不透明的。箭头或applicative functor中的术语(尽管注意,两者都允许您分别通过arrpure方法注入任意函数。)

因此,如果某些东西给定monad的特征(即使从概念上它形成一个),那么它可能更容易进行更大的检查和优化。序列化也可能更容易。因此,在解析器和电路建模中使用了应用程序和箭头。


上述尝试是一般性和描述性的。以下是一些我认为的经验法则。

如果您需要为看起来像状态的东西建模,请从monad开始。如果您需要对看起来像全局控制流的内容(即异常,延续)进行建模,请从monad开始。如果出现与monad的权力和普遍性相冲突的要求(即加入(join :: m (m a) -> m a)的功能太强大),那么考虑削弱你正在使用的东西的力量。

如果您需要对流进行建模,并对流进行转换,特别是对于某些特征(特别是过去和未来的无限视图)应该是不透明的流,则从应用程序仿函数开始。如果你需要更强有力的关于流的转换属性的推理,那么考虑到达箭头。

或者,非常粗略地说,应用程序用于电路的动作,箭头用于电路的结构,而monad用于通用计算效果。

故事当然还有很多。对于应用程序,请特别参阅Conal Elliott's work on FRP。对于箭头,请参阅HXT XML parser libraryYampa FRP projectHaskell on a Horse web framework,Hudak和Liu的经典"Plugging a Space Leak with an Arrow"论文等。对于monads,请到处看。当然要注意,仅仅因为某些东西是monad,这并不意味着应用符号可能不会更清晰,更具表现力。

答案 1 :(得分:15)

简短的回答是箭头比Monads更通用,使用起来也更麻烦。因此,你应该尽可能使用Monads,在Monads不适用的情况下使用Arrows。

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

引入Arrows的人John Hughes发表了两篇我推荐的优秀论文:“Generalising monads to arrows”“Programming with Arrows”。这两篇文章易于阅读并为您的问题提供答案。即使有些人不理解这两篇文章中的所有细节或代码,他们肯定会找到很多关于Monads和Arrows的信息和非常有用的解释。

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

当Monads被引入时,人们认为他们是全能的。事实上,Monads充满了力量。但在某些时候,人们发现有些情况下Monad无法应用。这些情况与多个输入有关,特别是当一些输入是静态的并且一些输入是动态的时。所以,John Hughes加紧介绍了Arrows。

箭头比Monads更通用。箭是莫纳德的超集。他们可以做Monad做的所有事情。但它们也更难以使用。 John Hughes建议您尽可能使用Monads,并且在不能使用Monads时应使用Arrows。

我同意约翰休斯的观点。我还想起了爱因斯坦的话:“一切都应该尽可能简单,但不要简单”。

当然,这一切都取决于手头的具体情况。让我解释。 让我们假设您正在学习Haskell。然后,使用monadic方法完成每个程序并使用基于箭头的方法重做它将是一项伟大的任务。当你学习时,你应该努力探索所有可能性并实施各种方法。通过这种方式,您可以获得很好的洞察力,并且您可以直接比较不同的解决方案。

现在让我们假设你想为社区提供一个库。那么,你欠那些会阅读你的代码的人,你应该使用最容易理解的方法并且仍然可以完成工作。您还应该使用您的代码的人员,您的解决方案缺乏不必要的复杂性。这样,您的解决方案更易于维护,并且不易出错和错误。

但如果你处于边缘情况怎么办?让我们假设你不确定你是否需要额外的箭头力量。那你该怎么办?如果需要,你应该从monadic方法开始,然后切换到基于箭头的方法吗?或者你应该从一开始就使用Arrows,避免在项目中途进行昂贵的转换?

同样,我的答案是尝试第一种方法:如果可以,尝试使用Monads。如果你后来发现你不能使用Monads,你将不得不忍受昂贵的开关,你必须重新启动并重做项目才能使用Arrows。这种方法肯定需要您的更多时间和其他资源。但是你会知道你做了正确的事情,那就是尝试提供最简单,最清晰,最简单的解决方案。

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