对于函数式编程来讲,1989/1990 是一个至关黑暗的年代。面向对象程序设计日益突出的表现,使得工业界对于函数式编程的关注愈来愈少。随着 John Hughes 的一篇论文"为何函数式编程如此重要"("Why Functional Programming Matters")的发布,它强力论证了主流思想在忽视函数式编程中可能犯的错误。这几乎是你惟一能听到的声音了。程序员
本文试图向大多数非函数式程序员去论述函数式编程的意义,并同时帮助函数式程序员发现其优点并加以利用。算法
我引用的那篇论文总共有23页,其中大部分的内容都是举例阐述做者的观点。它的中心论点至关简洁,讨论激烈,而且与咱们上周讨论的主题有关,这就是 模块化 。原谅文章开头部分的大量引用,这是由于 Hughes 的文章写的太好以至于不能省略。编程
如今人们广泛认为模块化设计是编程的关键。可是,有个很是重要的点容易被忽视。当用模块化编程去解决问题,须要把一个问题分解为子问题,解决全部子问题后再合并结果。分解原问题的方式直接取决于如何合并结果。所以,为了从概念上提高模块化能力,在编程语言中须要提供新的胶水能力( glue )。数据结构
但咱们一直在进步,Hughes 认为绝大多数人谈论到函数式编程的优点时,会常常讨论到:无反作用(side-effect free) 和 不包含赋值语句 (contain no assignment statements)。所以,表达式能够在任什么时候候计算并替换为其值, 这样程序就是引用透明的(referentially transparent)。即便到了今天也常常讨论计算时值不可变(value of immutability)和无反作用(side-effect free),固然,这些颇有价值。但对于不熟悉的人来讲,并非很好的方式去解释函数式编程(FP)。app
例如范畴论的"优点"很是明显,但若是其余人不认真了解它,就不会对此感到惊讶。它说了许多函数式编程没有的内容(没有赋值,没有反作用,没有控制流),可是并无说它的内容是什么。函数式编程听起来就像严守清规戒律的僧徒,牺牲了生活中的乐趣来但愿本身变得纯粹。对于物质利益更感兴趣的人来讲,这些优点彻底没有说服力。编程语言
使用函数式编程程序员会说,函数式编程是数量级更轻的一种,所以开发人员效率更高。ide
但为何会是这样?惟一可能的理由是传统编程中大概有90%的代码是赋值语句,这就是函数式编程优点的基础。在函数式编程中,赋值语句能够省略,这显然很荒谬。若是省略赋值语句带来了如此巨大好处,那么 FORTRAN 程序员可能20年来都这样作了。模块化
若是函数式编程这些特性不可以说服你,那么什么内容能既诠释函数式编程的威力,又指明函数式程序员所追求的方向呢?想一下结构化程序设计出现的年代,Hughes 总结它的优点可能能够归为一句话:结构化程序设计不包括 goto 语句。这与函数式编程所提的负面优点情形同样。函数式编程
过后来看,结构化程序设计这些特性虽然有用,但并无触及到问题的核心。结构化与非结构化程序设计最重要的不一样之处是: 结构化程序设计是一种模块化设计方式。模块化设计带来了极大生产力的提高:函数
缺乏goto语句有助于"小范围"编程,模块化则有利用"大范围"编程。如今咱们回到最初提的问题:分解问题的方式直接依赖于胶水粘合解决结果的方式。
接下来咱们会讲述函数式语言提供的两种新的,很重要的胶水能力。这是函数式编程能力的关键-它提升了模块化能力。它也是函数式程序员必须实现的目标 - 更小更简单更通用的模块,经过新的胶水能力粘合在一块儿。
两个新的胶水能力是
**高阶函数(Higher-order functions)**可以使简单函数粘合成更复杂的函数。我想大多数读者都熟悉这个想法。论文中的例子是foldr
,它在列表( list )上抽象了一个通用的计算模式,例如如下几个例子:
sum = foldr (+) 0
product = foldr (*) 1
anytrue = foldr or False
alltrue = foldr and True
length = foldr count 0 // where count a n = n + 1
map f = foldr (Cons .f) Nil
summatrix = sum . map sum
更多的例子。
这些例子总以让读者信服一点:模块化能够走的更远。将简单函数( sum )模块化为高阶函数和一些简单参数的组合,就获得高阶函数(foldr),能够用来编写列表中其余函数而不须要额外开发。
这不只仅只适用于列表( list ), 你能够为任何数据结构编写高阶函数。
全部这些均可以实现,由于函数在传统编程中不可分割,在函数式编程中可以表示为高价函数和特殊函数的组合。一旦定义好,高阶函数使一些操做更容易实现。不管什么时候定义一个新的数据类型,应该编写高阶函数处理它。这使得操做数据类型变得容易,而且将细节知识进行本地化表示。
**惰性求值( Lazy evaluation)**须要更多的思考去了解为何 Hughes 将它归为模块化的机制: 惰性求值使得模块化程序作为生成器成为现实,而且能构造大量可能的答案,选择器会选择合适的一个。没有惰性求值这些不会被实现。(若是可能,那要在有无限生成器的状况下)
咱们已经在函数语言上下文中描述了惰性求值,可是如此有用的特性应该加到非函数式语言中。惰性求值和反作用会共存么?不幸的是他们不能:在命令式符号中增长惰性求值是可能的,可是这种结合会让程序员的工程更加困难。
对于那些喜欢混合函数和非函数构造的语言,须要考虑一些问题。
为何它会让程序员的工做更困难呢?
由于惰性求值的威力须要程序员放弃程序执行时各部分之间顺序执行的控制能力,这会使带有反作用的编程变得困难,由于预测它们的发生顺序和是否发生,须要足够了解它们内嵌的上下文信息。这种全局依赖性将会破坏函数式语言中的模块性。惰性求值是为改善这种状况设计的。
下面一系列例子来证实惰性求值和高阶函数的威力: 牛顿-拉佛森平方根法; 数值微分与积分;评估博弈树的 alpha-beta 启发式算法。在全部的例子中,它表现出了如何迅速达到强大和富有表现力的抽象层次,使人印象深入,值得咱们仔细学习。
Why Functional Programming Matters John Hughes, Research Topics in Functional Programming, 1990 (based on an earlier Computer Journal paper that appeared in 1989).