Pure Function

Jayson Chiang
5 min readFeb 7, 2021

--

A pure function is a function that depends only on its declared inputs and its internal algorithm to produce its output. It does not read any other values from “the outside world” — the world outside of the function’s scope — and it does not modify any values in the outside world. — alvinalexander.com

Pure Function的兩個重點:

(1) output 只跟 input有關
(2) 沒有附作用

PF (Pure Funciton) 
= ODI (Output Depends on Input)
+ NSE (No Side Effects)

看下面的例子吧:

function sum(a, b) {
const result = a + b;
return result;
}
const x = 1;
const y = 2;
const answer = sum(x, y); // answer is 3;

有一個名稱為 sum 的function,return的值只來自參數的 a 跟 b (Output Depends on Input)。執行sum之後,得到新的變數為 3,而且原本的x 和y沒有改變 (No Side Effects)。這個 sum 就是 Pure Function。

再舉個例子,從反例開始:

假設有一組資料陣列 list :

const list = [
{ id:'a1', name:'apple', qty:2 }
{ id:'b2', name:'banana', qty:3 }
];

現在要在list中新增一筆資料:

const item = { id:'c3', name:'cherry', qty:0 }

讓結果的 list應該長這樣:

newlist = [
{ id:'a1', name:'apple', qty:2 },
{ id:'b2', name:'banana', qty:3 },
{ id:'c3', name:'cherry', qty:0 }
]

期望實作一個 funciton 叫 addItem ,可以滿足上述的需求:

// 反例(1)const addItem = (item) => {
list.push(item);
return list;
}
const newlist = addItem(item); // 執行後滿足原需求

這個例子的問題出在,第二行的 list 不是從 input得來,而是外部的變數。這樣就是 Depend on outside。因此調整過後的 function 應該把 list 放到 input裡:

// 反例(2)const addItem = (list, item) => {
list.push(item);
return list;
}
const newlist = addItem(list, item); // 執行後滿足原需求

上面的例子 return的結果完全 depend 於 input,讓 output的newlist 滿足需求,但同時也改變了原本變數list 所存的內容。我們稱這種 Side Effect,為了滿足 Pure Function的條件,addItem在執行後,list還是要跟本原的值一模一樣:

// Pure Functionconst addItem = (list, item) => {
const newlist = list.concat([item]);
return newlist;
}
const newlist = addItem(list, item); // 執行後滿足原需求

只要把 push 改成 concat 後,就可以得到新的結果,而且不會影響原本的 list。這就滿足 pure function 的定義。

那麼到底有哪些行為算是 Side Effect

  • 時間性質的函式,setTimeout等等
  • I/O:function 中執行系統存取
  • DB 資料庫存取相關
  • AJAX…
  • console.log …

其實還是有很多,但有些 Side Effect 其實並不嚴重,例如 console.log

那麼最後要說,為什麼要用Pure Function,它的好處是:

  • 可預測
  • 容易組合、分割、重用
  • 容易的測試、除錯
  • 可快取(cache)

關於快取,可以用下面的實例說明:

const sum = (x, y) => x + y; // Pure Function
const memoSum = memo(sum); // 把Pure Function打包成有快取的方法
memoSum(1, 2); // 3, 第一次呼叫,實際計算
memoSum(1, 2); // 3, 第二次以後,從快取拿結果

由於 sum是 Pure Function,所以每次計算完的結果,其實可以先存起來。之後有相同input的時侯,就不用重新計算,直接從快取吐結果就好。

memo的寫法如下 (momod是higher order funtion,但不是 Pure Function)。

const memo = (func) => {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if(!cache[key]) {
cache[key] = func.apply(func, args);
}
return cache[key];
}
}

--

--

No responses yet