為什麼要學習函數編程?

时间:2021-05-24 00:08:07

文 / 蔡學鏞

面對越來越複雜的軟體開發需求,我們需要更高的抽象層。過去二十多年,我們將物件導向(Object-Oriented Programming,OOP)和元件(Components)的觀念注入大多數的主流imperative編程語言(例如BASIC、C、Pascal),採用這些技術可以降低軟體開發的複雜度(Complexity),增加再用性(Reusability),於是我們著實過了好一陣子安穩的生活。

但軟體的複雜度就像細菌的抗藥性一樣,依然在持續增加。當萬古黴素開始壓制不住金黃色葡萄球菌,物件導向漸漸讓軟體開發力不從心,我們要如何是好?

越來越複雜的專業領域知識、越來越多元的異質平台、越來越普遍的網路環境和多核心電腦…這些都會提升軟體開發的複雜度,我們再度陷入困境。忽然發現,古老的函數編程(Functional Programming,FP)可以解決這個階段的許多問題,原來FP是現代編程問題的一帖良方。這使得最近函數編程忽然又被炒熱,成為許多語言設計者的最愛。

相較於OOP是往真實世界的方向進行抽象,FP卻是往數學的方向進行抽象。FP的理論基礎是來自Lambda Calculus,而Lambda Calculus同時賦予FP簡單性和威力。當然除了Lambda Calculus之外,方便的List處理、自動記憶體管理、Meta-Programming也都是函數式編程語言常見的特性。

提到函數編程,就不能不提1958年被創造出來的LISP,因為它是函數編程的始祖。暢銷書《駭客與畫家》(Hackers and Painters)的作者Paul Graham提到,LISP是許多公司成功的秘訣,因為透過LISP,他們可以用精簡的人力,在很短的時間內,開發出威力強大的系統。Paul Graham本來很擔心,他這麼一鼓吹LISP,要是別人都因此改用LISP,自己就佔不到便宜了。不過作者最後的結論是,大多數的人不會這麼聰明,所以應該可以不必擔心。他說的沒錯,在《駭客與畫家》與《Practical Common Lisp》出版約五年後的今天,LISP語言在Tiobe的語言需求排行榜卻不升反降,眼看就要被擠出前20名了,我直呼大師預言神準。

雖然Paul Graham稱讚的對象是LISP,但等於間接稱讚到FP。儘管一般程式開發人員對於FP的重視程度依然不高,但是語言設計者卻對FP興致高昂。既有的主流語言(例如C#、VB)開始在新版本中加入FP的特色;新興的語言(例如Scala、JavaFX)同時結合FP和OOP,就連古老的FP語言(例如ML)也有了新的面貌(例如F#)。

C#為了支援LINQ,以進行更高程度的資料查詢抽象,開始加入FP的特色。目前LINQ已經利用FP進行meta-programming,未來LINQ還會利用FP進行concurrent programming,以充分利用現代多核心的運算環境。

F#是衍生自ML家族的OCaml,融合了一些C#和Haskell的概念。許多時候,F#和OCaml的程式是可以交互編譯的,也就是說,兩個語言的相容性很高。由於F#是目前.NET平台最重要的FP語言(也是微軟官方提供的語言),因此未來隨著FP漸漸受到重視,F#也會佔有一席之地。

但我認為這「一席之地」恐怕坪數不大,卻公設很多。.NET環境下,函數式編程的語言選擇,我的建議是:如果你是基於科學、工程的目的而寫程式,那麼F#會比較適合,如果你要寫的是「世俗化的系統」(例如Web、資料庫、RIA),那麼還是使用C#吧。因為既有主流語言的使用者,可以漸進地學習函數編程。過去的經驗告訴我們,C++會流行、Java會流行,某一部份的因素是因為它們是從既有的語言去做加強(C++是C的強化版、Java是C/C++的改良版)。

但是我認為使用C#中語言的人,或許會因為使用LINQ之類的技術,而「間接」用到函數編程,但是很少有人在規劃軟體時會直接使用到函數編程的重要特色。「使用函數編程語言」不等於「進行函數編程」,你可以用函數編程語言寫出不合乎函數編程理念的程式,正如同你可以用物件導向語言寫出不合乎物件導向理念的程式。

更何況C#因為加入太多語言特色,變得過度複雜,開始有C++化的趨勢,而「複雜」是和FP的「簡單」背道而馳的。因此對於初學FP的人,或許F#等「新瓶舊酒」語言會更適合用來學習FP的觀念,培養相對單純的FP體驗。

真的想學好函數編程的觀念,可能還是得從傳統的FP語言著手,會比較徹底。函數編程只是一個觀念,各個語言的作法和語法差異可能不小,例如LISP家族、ML家族(F#屬於這個家族)、Erlang這三者都是相當重要的函數編程語言,但是這三種語言的差異相當大。如果你是.NET使用者,就學F#;Java使用者就學JavaFX或Scala;其他使用者就學Common Lisp或Erlang。

Paradigm-Shift總是痛苦的,但這是生存之道。從OOP的Paradigm轉移到FP,一開始可能會有不適應的地方,但熟悉之後,你會很高興你學會了一個新的技能、新的思維,可以面對新的環境、新的挑戰。

我知道函數編程當然也不會是軟體開發一勞永逸的解決之道,未來還有更多的挑戰會出現。或許,下次編程挑戰的救星,會是邏輯編程(Logic Programming)也說不定。