2008年5月31日 星期六

蔡學鏞【言程序】部落格: 思考函數編程(二)Why FP

蔡學鏞【言程序】部落格: 思考函數編程(二)Why FP:
儘管各種語言有差異,但是大致上來說,FP的共同點在於:「沒有副作用」(Side Effect)、「第一級函數」(First-Class Function)。「沒有副作用」是指在表示式(expression)內不可以造成值的改變;「第一級函數」是指函數被當作一般值對待,而不是次級公 民,也就是說,函數可當作「傳入參數」或「傳出結果」。

基本上,遵守上述兩點進行程式編寫,差不多就可以稱為FP。

遞迴可以保存狀態,可以讓程式變得相當精簡,但是成本(時間與記憶體)也很高。所以,許多時候,函數式語言會希望我們將程式寫成尾端遞迴(Tail Recursion),以便編譯器自動將它編譯成記憶體的直接跳躍(也就是迴圈)。

為了提昇效率,許多函數式語言會納入imperative的某些作法(例如允許副作用),這類的FPL被稱為不純(Impure)的函數式編程語言,例如 Ocaml、F#、LISP、REBOL。當然也有一些語言堅持Pure Functional的作法,例如Erlang、Haskell、Occam、Oz。主要是以Erlang為首的純函數式語言,似乎更能充分展現出FP的優勢。

這使得單元測試相當容易,只要管引數的結果正確與否就好,不需要管函數呼叫的次序正確與否,或者外部狀態是否做好正確的設定。如果是像C、Java或C#這類語言,檢查函數的傳出值是不夠的,因為函數執行過程中可能會改變外部狀態。但是對於FP來說,就不用擔心這一點。

想除錯,就必須能讓此錯誤可以重現(reproduce),然後定位(locate)錯誤的地方。對FP來說,由於沒有外部狀態的因素干擾,所以上述這兩 點都相當容易就可以做到。Erlang的某個函數只要會出錯,就一定每次都會出錯,所以可以「重現」;C語言的某個函數出錯,卻不見得每次都出錯,相當麻 煩。一旦知道某個函數出錯,你可以快速地在Erlang函數內找出問題所在,而予以修正;但是對C語言來說,外部狀態影響太多,不容易除錯。

FP 相當適合寫(concurrency)的程式。沒有共享記憶體,沒有執行緒,不需要擔心critical section,不必使用mutex等上鎖機制。由於沒有外部狀態的問題,FP的程式也相當適合進行程式碼「熱抽換」或「熱部署」(Hot Code Deployment) -- 你可以不需要關閉你的軟體系統,可以直接部署新的程式模組。

小P:FP相當適合寫concurrency的程式,在目前朝向多核心架構下,怪不得FP又熱了起來。

沒有留言: