點燈坊

學而時習之,不亦悅乎

使用 iif() 讓 Pipeline 不中斷

Sam Xiao's Avatar 2019-09-10

if else 在 Imperative 視為理所當然,但在 FP 卻是中斷 Pipeline 殺手,常常 Function 無法組合都是因為 if else 介入,透過 Wink-fp 的 iif(),可使 Pipeline 不中斷。

Version

macOS Mojave 10.14.6
VS Code 1.38.0
Quokka 1.0.243
Ramda 0.26.1
Wink-fp 0.0.4

Imperative

let fn = arg => {
  if (arg !== 1) return false;

  return 100;
};

fn(1); // ?
fn(2); // ?

實務上常需要對 argument 進行判斷,若不符合條件則使用 guard clause 提早 return false,符合條件則繼續。

iif000

let fn = arg => (arg === 1) && 100;

fn(1); // ?
fn(2); // ?

也可藉由 && 對 falsy value 的特性完成。

iif001

Functional

import { pipe } from 'ramda';

let iif = fn => arg => arg && fn();

let fn = pipe(
  x => x === 1,
  iif(() => 100)
);

fn(1); // ?
fn(2); // ?

&& 畢竟是 operator,無法 pipeline 使用,可將 &&iif() 實現,為了方便 point-free,將 data 放在最後一個 argument。

如此在 pipe() 時,iif() 就能接受前一個 function 傳來的 boolean 值,而不需要變數。

if002

Wink-fp

import { pipe } from 'ramda';
import { iif } from 'wink-fp';

let fn = pipe(
  x => x === 1,
  iif(() => 100)
);

fn(1); // ?
fn(2); // ?

Wink-fp 已經內建 iif(),可直接使用。

iif()
(a -> a) -> Boolean -> a
若 data 為 true,則執行 callback 並回傳其值

(a -> a):要執行的 function

Boolean:data 為 Boolean

a:回傳執行結果

iif() 本質為 && operator 的 function 封裝

iif003

Point-free

import { pipe, equals, always } from 'ramda';
import { iif } from 'wink-fp';

let fn = pipe(
  equals(1),
  iif(always(100))
);

fn(1); // ?
fn(2); // ?

pipe() 後,可將 callback 部分也順便 point-free。

iif004

Function Composition

import { compose, equals, always } from 'ramda';
import { iif } from 'wink-fp';

let fn = compose(
  iif(always(100)),
  equals(1)
);

fn(1); // ?
fn(2); // ?

既然能使用 pipe() 達成 pipeline,反向使用 compose() 就是 function composition 了。

Conclusion

  • Operator 乍看之下很討喜,其實都是中斷 pipeline 殺手,因為 operator 必定要搭配變數,但 pipeline 是不用變數的,因此可自行將這些 operator 包成 function,這就是 Wink-fp 的目的

Reference

Ramda, pipe()
Ramda, equals()
Ramda, always()
Ramda, compose()