點燈坊

失くすものさえない今が強くなるチャンスよ

如何實現 Pure Function ?

Sam Xiao's Avatar 2019-10-18

FP 三部曲 (Declarative、Pure Function 與 Immutable) 中的第二部曲就是 Pure Function,但也由於 ECMAScript 對於 Argument 有 Pass by Value 與 Pass by Reference 兩種,且型別並沒有完整支援 Immutable,因此實現 Pure Function 時有些 Tricky。

Version

macOS Catalina 10.15
VS Code 1.38.1
Quokka 1.0.256
ECMAScript 2015

Primitive

let data = 1;

let fn = x => x + 1;

fn(data); // ?
data; // ?

若 argument 為 primivite,因為 primitive 是以 pass by value 方式傳遞 argument,且 primitive 本身也是 immutable,因此實現 pure function 很直覺容易。

pure000

Array

let data = [1, 2, 3];

let fn = val => arr => {
  arr.push(val);
  return arr;
} 

fn(4)(data); // ?
data; // ?

若 argument 為 array 則有些 tricky,因為 ECMAScript 是以 pass by reference 傳遞 array,由於 Array.prototype.push() 是以 side effect 直接更改原本 array,而非 immutable,又因 pass by reference 間接修改既有資料,因此不是 pure function。

pure001

let data = [1, 2, 3];

let fn = val => arr => [...arr, val];

fn(4)(data); // ?
data; // ?

若要實現真正 pure function,則要改用 ES6 的 spread operator 展開,如此僅管 arr argument 是 pass by reference,但因為 spread operator 沒有 side effect 且 immutable,所以不會更改原本 array,也因此不會修改既有資料,因此為 pure function。

pure002

Object

let data = {
  name: 'Sam'
};

let fn = k => v => obj => {
  obj[k] = v;
  return obj;
}

fn('age')(18)(data); // ?
data; // ?

若 argument 為 object 也有些 tricky,因為 ECMAScript 是以 pass by reference 傳遞 object,由於 [] 是以 side effect 直接更改原本 object,而非 immutable, 又因 pass by reference 間接修改既有資料,因此不是 pure function。

pure003

let data = {
  name: 'Sam'
};

let fn = k => v => obj => ({...obj, ...{k: v}})
  
fn('age')(18)(data); // ?
data; // ?

若要實現真正 pure function,則要改用 ES6 的 spread operator 展開,如此僅管 obj argument 是 pass by reference,但因為 spread operator 沒有 side effect 且 immutable,所以不會更改原本 object,也因此不會修改既有資料,因此為 pure function。

pure004

Conclusion

  • 由於 ECMAScript 對於 primitive 與 array、object 有兩種完全不同處理方式,因此在實現 pure function 時不太一樣
  • ES5 早期有不少 side effect 寫法,ES6 則提供了 spread operator 避免 side effect