Monads与箭头
我对函数式编程中使用的monads和arrow的概念非常熟悉。 我也明白,他们可以用来解决类似的问题。
不过,在任何情况下如何select使用哪一个,我还是有点困惑。
我应该什么时候使用monads,什么时候应该使用箭头?
Lindley,Wadler&Yallop有两篇优秀论文(在LTU讨论)。
要理解的最重要的事情是,有更多的事物是箭头,而不是单子的东西。 相反,monads比箭头严格得多( 上面的第二篇文章明确指出了哪种方式)。
特别是,monad是装有函数types(a ~> b, a) ~> b
箭头,其中(~>)
是给定箭头的构造函数。 Lindley等人 指出这破坏了术语和命令之间的细致区分箭头(或者,如果你愿意的话,物体和态射)。
应用仿函数具有广泛的应用,特别是对于在stream上操作最好的东西。 实际上,人们可以将箭头看作是将stream变换的概念概括为stream(即在由给定的应用函数构造的对象上引入态射的新语言)。
根据我的经验,因为单子模糊物体和态射之间的区别(即,如果我正确地使用这些词,引起一个封闭的笛卡尔范畴),那么单子中的一个术语通常远比术语中的术语更不透明一个箭头或应用函数(尽pipe注意,两者都允许你分别通过arr
和pure
方法注入任意函数)。
因此,如果没有给出monad的特性(即使在概念上它是一个monad的特性),那么它可能对更大的检查和优化有潜在的开放。 序列化也很容易。 因此,在parsing器和电路build模中使用应用程序和箭头。
上面的尝试是一般的和描述性的。 以下是我的一些自以为是的经验法则。
如果你需要模拟一个看起来像状态的东西,那么从一个monad开始。 如果您需要模拟看起来像全局控制stream(即exception,延续)的东西,则从monad开始。 如果出现与monad的权力和普遍性(即join (join :: m (ma) -> ma)
过于强大)相冲突的要求,那么考虑削减你正在使用的东西的力量。
如果您需要对stream进行build模,并且在stream上进行转换,特别是某些特性(特别是对过去和未来的无限视图)的stream应该是不透明的,那么从一个应用函数开始。 如果你需要更强的关于溪stream转化特性的推理,那就考虑伸手去拿箭。
或者,粗略地说,应用是电路行为,箭头是电路结构,单子电路是通用计算效应。
当然,更多的故事。 对于申请人,请参阅Conal Elliott关于FRP的工作 。 有关箭头,请参阅HXT XMLparsing器库 , Yampa FRP项目 , 马web框架上的Haskell ,Hudak和Liu的经典“用箭头插入空间泄漏”论文等。 对于单子,无处不在。 当然要注意的是,仅仅因为事物是一个单子,并不意味着应用符号可能不会更清晰,更有performance力。
简单的答案是箭头比单子更普遍,使用起来也更麻烦。 因此,您应该尽可能使用Monads,在Monad不适用的情况下留下使用箭头。
下面是“风景路线”的答案。
介绍箭头的人约翰·休斯(John Hughes)发表了我推荐的两篇很棒的论文: “将单子推向箭头”和“用箭头编程” 。 这两篇文章很容易阅读,并为您的问题提供答案。 即使有些人不了解这两篇文章中的所有细节或代码,也肯定会find关于Monads和Arrows的大量信息和非常有用的解释。
现在我将强调这些文章中与您的问题有关的要点
当Monad被介绍时,人们认为他们是全能的。 事实上,Monad包含了很多权力。 但是在某些时候,发现有些情况Monad不能使用。 这些情况必须处理多个input,特别是当一些input是静态的并且一些input是dynamic的时。 所以,约翰·休斯加强了并且介绍了箭头。
箭比Monad更普遍。 箭头是Monads的超集。 他们可以做Monad做的所有事情。 但他们也更难以使用。 约翰·休斯(John Hughes)build议你应该尽可能地使用Monads,而当你不能使用Monads的时候你应该使用Arrows。
我同意约翰·休斯的观点。 我也想起了爱因斯坦的一句话:“一切尽可能简单,但不简单”。
当然,这一切都取决于眼前的具体情况。 让我解释。 让我们假设你正在学习Haskell。 然后,使用单点方法执行每个程序并使用基于箭头的方法对其进行重做将是一项艰巨的任务。 学习的时候,要努力探索各种可能性,实行各种办法。 通过这种方式,您可以获得很好的洞察力,并且可以直接比较不同的解决scheme。
现在让我们假设你想为社区提供一个图书馆。 那么,对于那些会阅读你的代码的人来说呢,你应该使用最容易理解的方法,而且仍然可以完成工作。 你也应该向那些会使用你的代码的人说,你的解决scheme缺乏不必要的复杂性。 这样,您的解决scheme更容易维护,不易出错和错误。
但是如果你处于临界情况呢? 让我们假设你不确定你是否需要箭的额外的力量。 那你应该怎么做? 你应该从一个单一的方法开始,如果需要的话,以后再切换到一个基于箭头的方法? 还是应该从一开始就从箭头开始,避免在项目中途进行昂贵的切换?
再次,我的答案是尝试第一种方法:尽可能使用Monads。 如果你以后发现你不能使用Monads,你将不得不忍受一个代价高昂的开关,在那里你将不得不重新启动并重做项目才能使用Arrows。 这种方法肯定会需要更多的时间和其他资源。 但是你会知道你做了正确的事情,试图提供最简单,最清晰,不那么复杂的解决scheme。
避免不必要的复杂性是最重要的。 不pipe你信不信,这就是把“分类理论”的概念(如函数组合,Monads和Arrows)引入计算机科学的原因。 讽刺?