函數編程 (functional programming) 1近幾年似乎漸漸跨出學術界,業界與黑客圈內對它的興趣也多了起來。每年參加 ICFP 的人越來越多,有不少是想來學習實用技術甚至發表結果的工程師。Epic Games 創始者,以 Unreal Engine 知名的 Tim Sweeney 在 POPL 06 應邀演講,以「下一個主流語言:一個遊戲設計者的觀點」為題,認為下一代的程式語言得要有效處理並行處理、模組化、與正確性的問題。解決之道呢?他認為函數語言應該要是預設的計算模型,並以強大的依值型別(dependent type)系統確保正確性。
這股風潮也吹到了中文世界。函數編程的中文資料已經不像以前那麼缺乏,簡體中文的翻譯與討論都不少。繁體中文圈倒是還得多加把勁。
對我來說,一方面高興有更多人對自己喜歡的東西有興趣,一方面卻得調適一下 — 好比喜愛的偏僻小鎮突然成了熱門觀光景點的那種不適應感。大家很熱情地談函數編程的好,這許多好處聽來倒讓我發現對這東西陌生了,一下子不知該怎麼回應才好。
開這個部落格時,我原本的打算大約也是如同英文首頁一樣,當作研究筆記用:做到哪就寫到哪,筆記著免得以後忘記了。所以才叫做小眾的計算科學嘛。經 Josh 提醒才認真想,我該不該寫個正式的函數編程介紹呢?
如果寫了,讀者是誰?以及是否還有這個必要?有歷史意義的文件中,John Hughes 流傳很廣的 Why Functional Programming Matters 仍歷久彌新,也有簡體中文譯本(但現在似乎不是很容易找到)。近來蔡學鏞也在他的部落格寫了一系列介紹函數編程的文章。我們還需要再多一篇嗎?
也許回到小鎮的比喻,就當作我在帶大家逛一些覺得不錯的小巷子吧。
純粹函數
如果物件導向是以物件為基本組成元素的程式設計觀念,函數程式設計 — functional programming 便是提倡以函數為基本單元來組織程式。這裡說的函數是純粹的數學函數:把一個輸入對應到一個輸出。如果我們用 fact
表示階層函數,fact 4
的值就是 1 × 2 × 3 × 4 = 24
,而且一直都如此(對比之下,C 的所謂 function 即使每次呼叫給同樣的參數,仍可能傳回不同的值)。
我們以往教程式設計時喜歡用盒子的比喻:一個變數是一個盒子,賦值(assignment)指令改變變數裡存放的東西,例如 x := x + 1
把變數 x
裡的值加一後再放回 x
。然而數學上我們從更基礎的概念出發,只談存在與否而先不談變化;x
如果存在而且是某個值,那它以後就一直是這個值了。表現在語言上,純粹的函數語言沒有賦值的動作。一個函數除了把輸入對應到輸出之外,其他不務正業的事情,如賦值、檔案讀寫、在螢幕上畫圖等等都被稱作「副作用」。純粹函數語言是不可以有副作用的。
事實上,若精確地說,純粹函數語言在使用副作用前得有一個明確地描述它們的方法。這我們以後再談。
負向列舉?
無論如何,由指令語言的觀點看函數語言,注意到的第一件事情是「函數語言裡面不可以改變數的值」。於是大家難免覺得奇怪,這怎麼會是個優點呢?自廢武功去掉一個功能,會有什麼好處呢?
Hughes 把這種情形和結構化程式設計做了比較。在結構化程式設計被人們了解之前,人們對它的印象是「不准用 GOTO
的程式設計。」這是一個負向的定義,談它「不是」什麼,但比較難看出它「是」什麼。此後關於結構化的討論一度變成了反方不斷找出「用 GOTO
會比較好」的程式,正方去解的寫程式比賽,直到大家漸漸了解結構化的目的是為了促進模組化。而結構化程式設計也不一定非用新設計的結構化語言不可。
函數編程亦然。我們需要一個好的談法來了解函數編程是什麼。
Pingback: Go To 有害大論戰