點燈坊

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

如何對 Getter 加以 Point-free ?

Sam Xiao's Avatar 2019-08-27

Vuex 的 Getter,其 State 與 Getter 都是以 Object 傳入,因此特別適合使用 Ramda 的 useWith() 使 Getter 加以 Point-free。

Version

macOS Mojave 10.14.6
Vue 2.6.10
Vuex 3.0.1
Ramda 0.26.1

Scenario 1

Normal Getter

let price = ({ isDiscount }, { price }) => isDiscount ? price : 0;

一個典型的 getter,state 與 getter 都是由參數傳入,我們會直接使用 destructing 在 argument list 拆解,且使用了 ?: ternary operator。

Point-free

import { useWith, ifElse, nthArg, always, prop } from 'ramda';

let price = useWith(
  ifElse(nthArg(0), nthArg(1), always(0)), [
    prop('isDiscount'),
    prop('price')
  ]
);

price() 主角為 isDiscount ? price : 0,此為 main function,在執行 main function 之前,必須透過 destructing 將傳進來的 state 與 getter 拆解,這正適合 useWith()

  • 使用 useWith() 先透過 prop() transformer function 將傳進來的 getter 與 setter 拆解
  • 將拆解後的值傳給 isShipable ? shipFare : 0 執行
  • ?: ternary operator 可改用 ifElse(),傳進來的參數可改用 nthArg() 獲得

如此 price() getter 就被 useWith() 加以 Point-free 了。

useWith()
((x1, x2, ...) -> z) -> [(a -> x1), (b -> x2), ...] -> (a -> b -> ... -> z)
建立一個與 main function 參數個數相同的新 function,且各參數會先經過 transformer function 處理過再傳給 main function 執行

Scenario 2

Normal Getter

let cash = ({ cash }, { amount }) => (cash >= amount) ? amount : cash;

與 use case 1 極為類似,差異在於 ?: ternary operator 的判斷再加碼使用 >=

Point-free

let cash = useWith(
  ifElse(gte, nthArg(1), nthArg(0)), [
    prop('cash'),
    prop('totalAmount'),
  ],
);

一樣使用 useWith()ifElse()

差異在於 ifElse() 目前不是接 nthArg(0),而是接 gte(),這相當於 >=,由於 gte() 本來就是兩個參數,因此直接接收兩個 transformer funciton 的回傳值。

gte()
Ord a => a → a → Boolean
若第一個參數大於第二個參數則傳回 true,否則傳回 false

Conclusion

  • 當參數為 object 時或 Vuex 的 getter 時,必須先對 object 加以拆解才能繼續使用,此時很適合使用 useWith() 透過 transformer function 加以處理,最後再傳給 main function

Reference

Ramda, useWith()
Ramda, ifElse()
Ramda, nthArg()
Ramda, prop()
Ramda, gte()