mik*_*era 70 language-agnostic monads haskell functional-programming arrows
我对函数式编程中使用的monad和箭头的概念非常熟悉.我也明白它们可以用来解决类似的问题.
但是,对于如何选择在任何给定情况下使用哪一个,我仍然有点困惑.
我什么时候应该使用monads?什么时候应该使用箭头?
scl*_*clv 73
Lindley,Wadler和Yallop有两篇优秀论文(LTU 在这里讨论过).
要理解的最重要的事情是,有更多东西是箭头,而不是有monad的东西.相反,monad比箭头更强大(上面的第二篇论文精确地指出了哪种方式).
特别地,monad是具有类型的apply函数的箭头(a ~> b, a) ~> b,其中(~>)是给定箭头的构造函数.林德利等人.指出这破坏了术语和命令之间保持的细致区分(或者,如果你愿意,还有对象和态射).
应用仿函数具有广泛的应用,特别是对于最好被认为是对流的操作的事物.事实上,人们可以将箭头视为在流上推广变换器概念而产生的(即在由给定的应用仿函数构造的对象上引入态射的新语言).
根据我的经验,因为monad模糊了对象和态射之间的区别(即,如果我正确地使用了单词,产生了一个封闭的笛卡尔类别),那么monad中的术语通常远比术语中的术语更不透明.一个箭头或一个applicative functor(虽然注意两者都让你分别通过arr和pure方法注入任意函数).
因此,如果没有给出monad的特征(尽管从概念上它形成了monad),那么它可能会对更大的检查和优化开放.序列化也可能更容易.因此,在解析器和电路建模中使用了应用程序和箭头.
以上尝试是一般性和描述性的.以下是一些我认为的经验法则.
如果你需要模拟看起来像状态的东西,请从monad开始.如果你需要建模看起来像全局控制流(即异常,延续)的东西,请从monad开始.如果出现与monad的权力和普遍性相冲突的要求(即连接(join :: m (m a) -> m a)过于强大),那么请考虑削减你正在使用的东西的力量.
如果您需要对流进行建模,并对流进行转换,特别是对于某些特征(特别是过去和未来的无限视图)应该不透明的流,请从应用程序仿函数开始.如果你需要更强有力的推理关于流的转换属性,那么考虑到达箭头.
或者,非常粗略地说,应用程序用于电路的动作,箭头用于电路的结构,而monad用于通用计算效果.
这个故事当然还有很多.对于应用程序,请参阅Conal Elliott特别关于FRP的工作.对于箭头,请参阅HXT XML解析器库,Yampa FRP项目,Haskell on Horse网页框架,Hudak和Liu的经典"用箭头插入空间泄漏"等文章.对于monads,请到处看.当然要注意,仅仅因为某些东西是monad,这并不意味着应用符号可能不会更清晰,更具表现力.
小智 15
简短的回答是箭头比Monads更通用,使用起来也更麻烦.因此,你应该尽可能使用Monads,在Monads不适用的情况下使用Arrows.
接下来是"风景路线"的答案.
介绍Arrows的人John Hughes发表了两篇我推荐的优秀论文:"将箭头推向箭头"和"用箭头编程".这两篇文章易于阅读并为您的问题提供答案.即使有些人不理解这两篇文章中的所有细节或代码,他们肯定会找到很多关于Monads和Arrows的信息和非常有用的解释.
我现在将重点介绍这些与您的问题相关的文章的要点
当莫纳德被引入时,人们认为他们是全能的.事实上,Monads充满了力量.但在某些时候,人们发现有些情况下Monad无法应用.这些情况与多个输入有关,特别是当一些输入是静态的并且一些输入是动态的时.所以,John Hughes加紧介绍了Arrows.
箭比Monads更通用.箭是莫纳德的超集.他们可以做Monad做的所有事情.但它们也更难以使用.John Hughes建议您尽可能使用Monads,并且在不能使用Monads时应使用Arrows.
我同意约翰休斯的观点.我还想起了爱因斯坦的话:"一切都应该尽可能简单,但不要简单".
当然,这一切都取决于手头的具体情况.让我解释.让我们假设您正在学习Haskell.然后,使用monadic方法完成每个程序并使用基于箭头的方法重做它将是一项伟大的任务.当你学习时,你应该努力探索所有可能性并实施各种方法.通过这种方式,您可以获得很好的洞察力,并且您可以直接比较不同的解决方案.
现在让我们假设您想为社区提供一个库.那么,你欠那些会阅读你的代码的人,你应该使用最容易理解的方法并且仍然可以完成工作.您还应该使用您的代码的人员,您的解决方案缺乏不必要的复杂性.这样,您的解决方案更易于维护,不易出错和出错.
但是,如果你处于边缘情况怎么办?让我们假设你不确定你是否需要额外的箭头力量.那你该怎么办?如果需要,你应该从monadic方法开始,然后切换到基于箭头的方法吗?或者你应该从一开始就使用Arrows,避免在项目中途进行昂贵的转换?
同样,我的答案是尝试第一种方法:如果可以,尝试使用Monads.如果你后来发现你不能使用Monads,你将不得不忍受昂贵的开关,你必须重新启动并重做项目才能使用Arrows.这种方法肯定需要您的更多时间和其他资源.但是你会知道你做了正确的事情,那就是尝试提供最简单,最清晰,最简单的解决方案.
避免不必要的复杂性是最重要的.信不信由你,这就是将类别理论中的概念(如函数组合,Monads和Arrows)引入计算机科学的原因.讽刺?