Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate. Instead the definitions merely compose other functions, among which are combinators that manipulate the arguments. -wiki
當我們在寫函式時一定如下圖,有輸入的 data a
,然後在 fn 裡做一堆運算跟加工後輸出結果 b
基本上中間運算 fn 不能有 I/O 操作,需要是 pure 的,但當運算越變越複雜,我們就會拆成好幾個小 function
上面兩種都是輸出 a 得到 b,可以觀察到若先定義好 fa
、 fb
、 fc
、 fd
就可以運算出 fn
,整個過程根本不需要 a
或 b
,這就是 Pointfree。
Pointfree 的 point 也就是指函式的參數,所以 Pointfree 就是沒有參數的意思很好理解。Pointfree 是 FP 的應用;使用各式的 pure function 組合成我們要做的事,當然這些 function 都不包含資料的處理。數據流與程式行為分離開來,使我們只關注在做什麼 What,而非怎麼做 How (如何處理數據)。
簡單的說就是:「Pointfree 不會跟你說 Data 是什麼。」
Pointfree 讓運算過程抽象化,處理一個值,但根本不用提到他。這樣寫譨讓程式碼閱讀性更高也越符合語意,更容易重覆使用,測試也變得更容易。直接用 Composition 說過從牛變成漢堡肉的例子
const 肉片機器 = input => 生牛肉片
const 絞肉機器 = input => 牛絞肉
const 加工機器 = input => 加工程漢堡肉
const 煮熟機器 = input => 煮熟
const compose = (a, b, c) => x => a(b(c(x)))
let 製造漢堡肉 = compose(煮熟機器, 加工機器, 絞肉肉機器, 肉片機器)
// 先執行 肉片機器 然後 絞肉肉機器 然後以此類推
製造漢堡肉(牛); // 輸出漢堡肉
會拆分成許多小 function ,每一個 function 就像一根根小水管,小水管之間互相獨立,也遵守 one input, one outtput 概念 (Pure),最後再接再一起。可以觀察定義 製造漢堡肉
時
let 製造漢堡肉 = compose(煮熟機器, 加工機器, 絞肉肉機器, 肉片機器)
完全沒有處理到任何資料(牛),只是把不同 function 組合在一起,這就是 Pointfree。Pointfree 可以用一些通用的函式組合成各式複雜的運算,也可以一直使用不同排列組合 reuse 這些通用函式,達到不同結果
以下是輸入 魚
得到魚丸,跟輸入 章魚
得到 章魚燒
。
蛤?那要怎麼寫程式啊,其實只要運用前面幾篇所講的 curry
(只吃一個參數)、commpose
就可以做到了。而 Ramda 的自動 Curry 化與 "Function first,Data last" 也更很容易寫出 Pointfree Style 的程式碼。
const isWorker = s => s === 'worker';
const propRole = R.prop('role');
const getWorkers = R.filter(R.pipe(propRole, isWorker));
var data = [
{name: '张三', role: 'worker'},
{name: '李四', role: 'worker'},
{name: '王五', role: 'manager'},
];
getWorkers(data)
// [
// {"name": "张三", "role": "worker"},
// {"name": "李四", "role": "worker"}
// ]
定義 getWorkers
的时候,完全没有提到 data,这就是 Pointfree。
其實我也是第一次知道 Pointfree,寫起來其實蠻抽象化需要適應,但真的能夠寫出很語易化的程式碼,特別要謝謝參考文章中的作者,讓我們這些後輩能夠看這些文章就可以學習到一種新觀念
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。