[JS] Functional Programming and Currying
Functional Programming 是一種程式撰寫的風格,會把函式當成參數傳入,並以沒有 side effect 的方式回傳另一個函式。這也衍伸了一些概念:
- Pure Function
- Currying
- Higher-Order Function
什麼是 Pure Function
Pure function 有兩個明確的定義:
- 當帶入相同的參數時,一定會得到相同的結果,又稱作 deterministic。
- 它不會造成任何可觀察到的副作用(observable side effects),所有的變數多是 immutable 的。
當一個函式是 pure function 且 immutable data,那麼我們可以稱作 referential transparency
immutability 指的是不會隨著時間改變。
什麼是 Currying
Currying 是 functional programming 中的一種過程,我們會將函式當成參數帶入另一個函式的參數中,因此變成嵌套的函式(nesting functions),而這個函式會回傳一個新的函式。
在 Currying 中,會把帶有多個參數(arity)的函式,轉換成一次帶一個參數的多個連續函式。
// 2-arity function
function fn(a, b) {
//...
}
// 3-arity function
function _fn(a, b, c) {
//...
arity:指的是一個函式有的參數數目,近似 arguments
將一般的函式轉成 Currying function:
// 原本的函式帶有三個 arity
function multiply(a, b, c) {
return a * b * c;
}
multiply(1, 2, 3); // 6
轉成 currying function 後:
const multiply = (a) => (b) => (c) => {
return a * b * c;
};
multiply(1)(2)(3); // 6
// 等同於下面的寫法
function multiply(a) {
return function (b) {
return function (c) {
return a * b * c;
};
};
}
currying 背後的概念就是根據一個函式產生一個加料後的函式(specialized function)。
什麼是 Partial Function Application
在 currying 中,每一個函式都只會接收一個參數,但在 partial function application 中,一個函式可能會接收超過一個以上的參數:
// Partial Function Application
const volume = (h) => (w, l) => {
return h * w * l;
};
console.log(volume(70)(90, 30));
partial application 和 currying 是相關的,但設計邏輯上並不一樣,兩者一樣的地方在於,它們都依賴閉包(closure)才能作用。
Currying 的好處
為某一個函式設定設置
例如,我們有一個計算折扣的函式,在還沒使用 currying 之前,可以寫成這樣:
function getDiscountPrice(price, discount) {
return price * discount;
}
let price = getDiscountPrice(100, 0.9);
這個的麻煩之處在於,即時全館 9 折,但我們在使用這個函式時每次都還是要把 discount 代到參數中。如果我們使用 currying 則可以制定一個專門就是 9 折用的函式:
const getDiscountPrice = (discount) => (price) => {
return price * discount;
};
// 都是九折的情況
const get10PercentOff = getDiscountPrice(0.9);
let price = get10PercentOff(100); // 90
Higher Order Function
當我們談到 higher-order functions 時,我們指的是:
- 接受一個以上的函式當作參數
- 最後會回傳出一個函式