對於函數式編程來講,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).