雜談
Hi,我是PK還在廢,因為工作的關係這邊接觸到Functional Programming (FP)的Programming Paradigm,聽說以前大家都是寫Object Oriented Programming (OOP),那為何要轉為使用FP呢?,由於覺得很新奇,決定來花時間記錄一下學習過程。
在接觸FP前,由於不是資工相關背景出身的,其實對Programming Paradigm沒有很在意,畢竟多數做的是一人專案,只要能動就好(X。不過開始工作後,變為多人團隊開發,因此對於統一的Programming Paradigm有較高的要求,方便提升程式可讀性與持續維護能力。
什麼是Functional Programming?
Function is the first-class citizen in FP
First-class citizen這個概念在Programming領域可以理解為做為一個參數傳入,或可以儲存為一個變數,所以可以理解為FP可以把Function作為變數儲存,並且可以傳遞至下一層繼續進行運算。但這樣有有什麼好處呢? 別急,後面會慢慢說明。
當一個Function的參數也包含一個Function時,我們稱這個Function為High Order Function,這種類型的Function充斥於FP內,是實現FP的基礎核心概念,以下來個簡單的範例。
// return a function
const add = (firstNumber: number) => (secondNumber: number):number {
return (firstNumber + secondNumber)
}
// store function as const
const addOne = add(1)
const addFive = add(5)
console.log(addOne(5)) // output: 6
console.log(addFive(5)) // output: 10
那將Function的作為參數代入有什麼好處呢? 簡單來說就是模組化設計,透過將重複的Function包裝,可以在需要時重新組合成新的功能,這使得程式的設計更加彈性,且易於擴展。
Side Effect
FP還有一個很重要的概念,那就是Immutable,目的是為了避免參數、變數等等的狀態改變造成的Side Effect。
Side Effect是程式開發非常忌畏的影響,因為當程式結構一大,你會搞不清楚哪邊造成了狀態變動,導致錯誤排除困難。在JavaScript中常見的Side Effect有以下幾種 :
- 更改外部變數與物件屬性 (例如: Global or Parent Variable)
- 將值寫入consolo.log或檔案內
- 觸發外部流程 (multi thread, API)
- 呼叫任何有Side Effect的Function
FP的一大優勢就是避免Side Effect發生,而當Function沒有Side Effect,我們會稱之它為Pure Function ,當你輸入相同的Input,那你會獲得相同的Output。
和Object Oriented Programming的差異?
其實兩種模式各有優缺,且兩種模式要共用也是可行的,因此直接拿兩者比較並沒有太大的意義。OOP如果參照Don’t Repeat Yourself (DRY)原則也是可以寫出類似FP效果,差異在於FP的Immutable有強制性規範,而OOP對此較為寬容。
此外,對於OOP而言,將Class作為基本單元來管理程式,而FP則是以Function為基本單元來管理,所以可以依據你們團隊的開發偏好來選擇適當的Programming Paradigm。
有興趣可以參考這篇文章下面的留言,應該會有近一步的認識 :
Declarative & Imperative Paradigm
在談到FP和OOP時,會提到Declarative (宣告式) 和 Imperative (命令式) 這兩種模式 :
- Declarative Paradigm : 告訴用戶What to do,用戶可以透過自然語言理解現在這個階段在執行什麼操作,常見的有 : SQL, HTML等語法,FP也是屬於這種類型。
- Imperative Paradigm : 告訴用戶How to do,用戶主要透過State來判斷如何操作,一般我們寫程式用到
for
,switch
,while
,if…else
就是屬於這類,多數程式語言屬於此類,也因此會有OOP是此類的說法。
const arr: number[] = [3, 2, 1]
// Declarative paradigm
const add5forArrayDeclarative = (array: number[]): number[] =>
array.map((element: number) => element + 5)
// Imperative paradigm
const add5forArrayImperative = (array: number[]): number[] => {
const newArray: number[] = []
for(var i=0; i < array.length; i++) {
newArray.push(array[i] + 5)
}
return newArray
}
console.log(add5forArrayDeclarative(arr)) // output: [8, 7, 6]
console.log(add5forArrayImperative(arr)) // output: [8, 7, 6]
Declarative較依賴Expression (可以直接理解為Function),Expression是一種單純的運算式,上面使用的map
就是一種,並且這種運算式一定會回傳值,讓我們可以把回傳值送至下個階段進行運算,所以也可以說是Data Flow的控制。
透過這種特性,我們可以將多個Expression串接,構建複雜的Function,並且由於不會有Side Effect,因此程式複雜度不會上升太多,這也是FP的一大優勢。不過這邊也可以很明顯看出FP的導入困難之處,那就是學習曲線。
From the amazing Mostly Adequate FP Guide
要熟習使用FP需要找到相對的套件,並且熟悉裡面提供的Expression,才有辦法發揮100% FP的優勢。這也是多數公司沒有導入FP的原因之一,專案交付看不懂那不是更麻煩嗎?
(ಠ益ಠ)
Conclusion
今天先簡單帶過Functional Programming的基礎概念,方便大家後面理解,也同時提供大家分析看看究竟是FP還是OOP更加適合現在的團隊開發。這個答案沒有絕對,也不是誰好誰壞的二元題目,唯有選擇適合自己團隊的開發模式,才能維持好的專案品質( • ̀ω•́ )。
下篇預計會介紹如何實現FP,以及FP中常常被使用到的概念與Expression,敬請期待。
Reference
-
testing
Leave a Reply