简体   繁体   English

单子 vs. 箭头

[英]Monads vs. Arrows

I'm broadly familiar with the concepts of monads and arrows as used in functional programming.我对函数式编程中使用的monad箭头的概念非常熟悉。 I also understand that they can be used to solve similar kinds of problems.我也明白它们可以用来解决类似的问题。

However, I'm still a bit confused about how to select which one to use in any given situation.但是,对于在任何给定情况下如何选择使用哪个,我仍然有些困惑。

When should I use monads and when should I use arrows?什么时候应该使用 monad,什么时候应该使用箭头?

There are two excellent papers by Lindley, Wadler & Yallop (discussed at LTU here ). Lindley、Wadler 和 Yallop 发表了两篇出色的论文(在 LTU此处讨论)。

The most important thing to understand is that there are more things which are arrows than there are things which are monads.需要理解的最重要的事情是,有更多的东西是比箭有东西是单子。 Conversely, monads are strictly more powerful than arrows (the second paper above specifies precisely in which fashion).相反,monad 严格来说比箭头更强大( 上面第二篇论文精确地指定了哪种方式)。

In particular, monads are arrows equipped with an apply function of type (a ~> b, a) ~> b , where (~>) is the constructor for a given arrow.特别是,monad 是配备了类型为(a ~> b, a) ~> b的 apply 函数的箭头,其中(~>)是给定箭头的构造函数。 Lindley et al.林德利等人。 point out that this destroys the meticulous distinction arrows maintain between terms and commands (or, if you prefer, objects and morphisms).指出这破坏了箭头在术语和命令(或者,如果您愿意,对象和态射)之间保持的细致区分。

Applicative functors have a wide variety of applications, particularly for things which are best thought of as operations on streams.应用函子有广泛的应用,特别是对于那些最好被认为是对流的操作的东西。 One can in fact think of arrows as arising from generalizing the notion of a transformer on streams (ie introducing a new language for morphisms on objects constructed by a given applicative functor).事实上,人们可以将箭头看作是在流上推广转换器的概念(即,为由给定的应用函子构造的对象上的态射引入一种新语言)。

In my experience, because monads blur the distinction between objects and morphisms (ie, if I'm using the words correctly, give rise to a closed cartesian category), then a term in a monad is generally far more necessarily opaque than a term in an arrow or applicative functor (although note that both let you inject arbitrary functions by the arr and pure methods respectively).根据我的经验,由于 monad 模糊了对象和态射之间的区别(即,如果我正确使用了这些词,则会产生一个封闭的笛卡尔范畴),那么 monad 中的术语通常比一个箭头或应用函子(尽管请注意,两者都允许您分别通过arrpure方法注入任意函数)。

So if something is not given the characteristics of a monad (even though conceptually it forms one), then it is potentially open to greater inspection and optimization.因此,如果某些东西没有被赋予 monad 的特征(即使在概念上它形成了一个),那么它就有可能接受更多的检查和优化。 It is also potentially easier to serialize.序列化也可能更容易。 Hence the use of applicatives and arrows in parsers and circuit modeling.因此在解析器和电路建模中使用了应用程序和箭头。


The above attempted to be general and descriptive.以上试图是一般性和描述性的。 Below are some of my opinionated rules of thumb.以下是我的一些自以为是的经验法则。

If you need to model something that looks like state, start with a monad.如果您需要对看起来像状态的东西进行建模,请从 monad 开始。 If you need to model something that looks like global control flow (ie exceptions, continuations), start with a monad.如果您需要对看起来像全局控制流(即异常、延续)的东西进行建模,请从 monad 开始。 If a requirement arises that conflicts with the power and generality of monads (ie for which join (join :: m (ma) -> ma) is too powerful), then consider chipping away at the power of the thing you're using.如果出现与 monad 的功能和通用性相冲突的需求(即对于哪个 join (join :: m (ma) -> ma)过于强大),那么请考虑削弱您正在使用的东西的功能。

If you need to model streams, and transformations on streams, and particularly streams for which certain characteristics (particularly unlimited views of the past and future) should be opaque, then start with an applicative functor.如果您需要对流和流上的转换进行建模,特别是某些特征(尤其是过去和未来的无限视图)应该是不透明的流,那么从应用函子开始。 If you need stronger reasoning about properties of transformations on streams, then think about reaching for an arrow.如果您需要对流上的转换属性进行更深入的推理,请考虑使用箭头。

Or, very crudely put, applicatives are for the actions of circuits, arrows are for the structures of circuits, and monads are for general-purpose computational effects.或者,非常粗略地说,applicative 用于电路的动作,箭头用于电路的结构,而 monad 用于通用计算效果。

There's of course much more to the story.当然还有更多的故事。 For applicatives, see Conal Elliott's work on FRP in particular.对于应用程序,请特别参阅Conal Elliott 在 FRP方面的工作 For arrows, see the HXT XML parser library , the Yampa FRP project , the Haskell on a Horse web framework , Hudak and Liu's classic "Plugging a Space Leak with an Arrow" paper, among other things.对于箭头,请参阅HXT XML 解析器库Yampa FRP 项目Haskell on a Horse Web 框架、Hudak 和 Liu 的经典文章 “用箭头堵塞空间泄漏”等。 For monads, see everywhere.对于 monad,请查看任何地方。 And of course take note that just because something is a monad, that doesn't mean that applicative notation might not be clearer and more expressive.当然,请注意,仅仅因为某物是 monad,并不意味着应用符号可能不会更清晰和更具表现力。

The short answer is that Arrows are more general than Monads and are also more cumbersome to use.简短的回答是 Arrows 比 Monads 更通用,而且使用起来也更麻烦。 Thus you should use Monads whenever you can, leaving the use of Arrows for the cases where Monads are not applicable.因此,您应该尽可能使用 Monads,在 Monads 不适用的情况下使用 Arrows。

The “scenic route” answer follows. “风景路线”的答案如下。

John Hughes, the person who introduced Arrows, has published two great papers that I recommend: “Generalising monads to arrows” and “Programming with Arrows” .介绍 Arrows 的人 John Hughes 发表了两篇我推荐的伟大论文: “将单子推广到箭头”“用箭头编程” These two articles are easy to read and provide the answer to your question.这两篇文章易于阅读,并为您的问题提供了答案。 Even if some people do not understand all the details or the code in these two articles, they will certainly find lots of information and very helpful explanations about Monads and Arrows.即使有些人不理解这两篇文章中的所有细节或代码,他们肯定会找到很多关于 Monads 和 Arrows 的信息和非常有用的解释。

I will now highlight the main points from these articles that pertain to your question我现在将强调这些文章中与您的问题有关的要点

When Monads were introduced, people thought that they were all-powerful.当 Monads 被引入时,人们认为它们是无所不能的。 Indeed, Monads pack a lot of power.确实,Monads 具有强大的功能。 But at some point, it was found that there are cases where Monads cannot be applied.但是到了某个时候,发现有些情况下,Monads 是不能应用的。 These cases have to do with multiple inputs, especially when some of the inputs are static and some of the inputs are dynamic.这些情况与多个输入有关,尤其是当某些输入是静态的而某些输入是动态的时。 So, John Hughes stepped up and introduced Arrows.因此,约翰·休斯(John Hughes)挺身而出,介绍了 Arrows。

Arrows are more general than Monads.箭头比 Monad 更通用。 Arrows are a superset of Monads.箭头是 Monad 的超集。 They can do all that Monads do and more.他们可以完成 Monad 所做的一切,甚至更多。 But they are also more difficult to use.但它们也更难使用。 John Hughes recommends that you should use Monads whenever you can and that you should use Arrows when you cannot use Monads. John Hughes 建议你应该尽可能使用 Monads,当你不能使用 Monads 时应该使用 Arrows。

I agree with John Hughes.我同意约翰休斯的观点。 I am also reminded of Einstein's quote “Everything should be made as simple as possible, but not simpler”.我还想起了爱因斯坦的名言“一切都应该尽可能简单,但不能更简单”。

Of course, it all depends on the particular situation at hand.当然,这一切都取决于手头的特定情况。 Let me explain.让我解释一下。 Let us suppose that you are learning Haskell.让我们假设您正在学习 Haskell。 Then it would be a great task to do each program using a monadic approach and redo it using an arrow-based approach.然后使用一元方法完成每个程序并使用基于箭头的方法重做它将是一项伟大的任务。 When you learn, you should strive to explore all possibilities and implement all kinds of approaches.在学习的时候,你应该努力探索所有的可能性,实现各种方法。 This way, you obtain great insight and you are able to compare different solutions first-hand.通过这种方式,您可以获得深刻的洞察力,并且能够直接比较不同的解决方案。

Now let us suppose that you want to provide a library to the community.现在让我们假设您想向社区提供一个库。 Well, you owe it to the people that will read your code that you should use the approach that is easiest to understand and still gets the job done.好吧,您应该感谢会阅读您代码的人,您应该使用最容易理解的方法并且仍然可以完成工作。 You also owe it to the people that will use your code that your solution lacks unnecessary complexity.您还应该感谢将使用您的代码的人,您的解决方案缺乏不必要的复杂性。 This way, your solution is more easily maintainable and less prone to errors and bugs.这样,您的解决方案更易于维护,并且更不容易出错和出错。

But what if you are in a borderline case?但是,如果您处于边缘情况怎么办? Let us suppose that you are not sure whether or not you will need the extra power of Arrows.让我们假设您不确定是否需要 Arrows 的额外力量。 Then what should you do?那你应该怎么做? Should you start with a monadic approach and later switch to an arrow-based one if the need arises?您是否应该从一元方法开始,然后在需要时切换到基于箭头的方法? Or should you start with Arrows from the get-go, avoiding a costly switch halfway through the project?还是应该从一开始就使用 Arrows,避免在项目中途进行昂贵的转换?

Again, my answer is to try the first approach: try to use Monads if you can.同样,我的答案是尝试第一种方法:如果可以,尝试使用 Monads。 If you later find out that you cannot use Monads, you will have to endure a costly switch, where you will have to restart and redo the project in order to use Arrows.如果你后来发现你不能使用 Monads,你将不得不忍受代价高昂的转换,你必须重新启动并重做项目才能使用 Arrows。 This approach will certainly require more time and other resources from your part.这种方法肯定需要您提供更多的时间和其他资源。 But you will know that you did the correct thing, which was to try to provide the simplest, clearest, less complex solution possible.但是你会知道你做了正确的事情,那就是试图提供最简单、最清晰、最简单的解决方案。

Avoiding unnecessary complexity is the most important thing.避免不必要的复杂性是最重要的。 Believe it or not, this is the reason concepts (such as function composition, Monads and Arrows) from Category Theory were introduced to Computer Science.信不信由你,这就是将范畴论中的概念(例如函数组合、Monads 和 Arrows)引入计算机科学的原因。 Ironic?讽刺?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM