將傳入 Data 的 Function 加以 Point-free,這在 Ramda 很常見;但若 Data 傳入的目的是產生另外一個 Function,這類 Higher Order Function 可使用 ncurryN()
使其 Point-free。
Version
macOS Mojave 10.14.6
VS Code 1.38.1
Quokka 1.0.253
Ramda 0.26.1
Higher Order Function
let data = { isShipable: true };
let genShipFare = shipFare => obj => obj.isShipable ? shipFare : 0;
genShipFare(80)(data); // ?
一個非常典型的 higher order function,以 shipFare
為 argument,傳回另外一個以 obj
為 argument 的的 function。
ifElse()
import { ifElse, prop, always } from 'ramda';
let data = { isShipable: true };
let genShipFare = shipFare => ifElse(
prop('isShipable'),
always(shipFare),
always(0)
);
genShipFare(80)(data); // ?
關於將 data 加以 point-free,Ramda 非常在行,改用 ifElse()
之後,obj
就被 point-free 掉了。
obj
不用於產生 function,被 point-free 掉合情合理,shipFare
是用來產生 function 的重要 argument,也有可能被 point-free 掉嗎 ?
pipe()
import { ifElse, prop, always, pipe, __ } from 'ramda';
let data = { isShipable: true };
let genShipFare = pipe(
always,
ifElse(prop('isShipable'), __, always(0))
);
genShipFare(80)(data); // ?
pipe()
是實務上最常用的 point-free 手段,若原 function 的最後一個 argument 要 pointfree,直接 pipe()
就可以;但若是其他 argument,會搭配 __()
與 always()
,其中 always()
可確保 arity 為 1
。
uncurryN()
import { ifElse, prop, always, uncurryN, useWith, __, identity } from 'ramda';
let data = { isShipable: true };
let ifElse4 = uncurryN(4, ifElse);
let genShipFare = useWith(
ifElse4(prop('isShipable'), __, always(0)), [
always,
identity
]
);
genShipFare(80)(data); // ?
其實回想一下 genShipFare()
,ifElse()
才是 main function,其他 function 只是為了 ifElse()
跑龍套用。
當要 point-free 時,首先會想到 useWith()
,然後 main function 使用 ifElse()
。
第 5 行
let ifElse4 = uncurryN(4, ifElse);
但問題來了,ifElse()
的 arity 為 3
,且回傳是 function,根本不含 data 部分,因此先使用 uncurryN(4)
將原 ifElse()
變成 4 個 argument 的 ifElse4()
,為能接 data 鋪路。
ifElse4(prop('isShipable'), __, always(0)), [
always,
identity
]
ifElse4()
的第一個與第三個 argument 毫無懸念,但第二個 shipFare
以 __
取代,最後一個 argument 是 data,所以是 identity()
。
整體思維就是使用
uncurryN()
暴力將ifElse()
增加一個 argument,使之能搭配useWith()
成為 point-free
Conclusion
- 要使 higher order function 也 point-free 有幾個 pattern:
pipe()
、uncurryN()
pipe()
:若 argument 為了決定 higher order function,通常不可能是最後一個,因此會使用__
墊出要 point-free 的參數,再使用always()
決定參數的 arityuncurryN()
:動態將 main function 增加一個 argument 傳 data,再使用useWith()
,identity()
則為 data 部分
Reference
Ramda, ifElse()
Ramda, prop()
Ramda, always()
Ramda, uncurryN()
Ramda, useWith()
Ramda, always()
Ramda, __()
Ramda, identity()