iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0
Software Development

Should I use fp-ts系列 第 6

[Should I use fp-ts?] Day 06 - pipe and flow

  • 分享至 

  • xImage
  •  

在本系列文中,所有的程式碼以及測試都可以在 should-i-use-fp-ts 找到,今日的範例放在 src/day-06 並且有習題和測試可以讓大家練習。

pipe and flow

接下來要開始接觸 fp-ts 之中常常使用到的工具了 pipe, flow,這邊繼續使用昨天的函式以及變數來進行解說。

昨天的 function 最後是這個形式。

list = [1, 2, 3, 4, 5];
const inc = (x: number): number => x + 1;
const toString = (x: number): string => x.toString();
// point
list.map(x => inc(x))
	.map(x => toString(x));
// pointfree
list.map(inc)
	.map(toString);

這邊的函式重複使用了 map 兩次,但這邊其實只要一次 map 即可完成邏輯。

// point
list.map(x => toString(inc(x)));
// pointfree error
list.map(toString(inc)); // Error

這樣的話只要一次迴圈就可以處理完畢,但這邊無法使用 pointfree ,因為這樣子寫並無法正確傳入變數,如果要使用 pointfree 的話可能就需要先將函數 組合 起來。

const incAndToString = (x: number): string => toString(inc(x));
list.map(incAndToString);

incAndToString 的泛用程度明顯會比 inc 或是 toString 還要低的多,為這個處理特別產生一個函式並不是個優雅而且簡潔的寫法,如果可以製作一個專門 組合 函式的函式就可以解決這個問題。
一樣用上述的例子來製作組合函數 flow:

const inc = (x: number): number => x + 1;
const toString = (x: number): string => x.toString();
const incAndToString = (x: number): string => toString(inc(x));
// 製作一個 flow2 來組合 inc 和 toStirng 兩個函數
// arrow function style
type Flow2 = (f: (x: number) => number, g: (x: number) => string) 
		   => (x: number) => string
const flow2: Flow2 = (f, g) => x => g(f(x));
// funtion style
function flow2F(f: (x: number) => number, g: (x: number) => string) {
  return function (x: number) {
    return g(f(x));
  };
}

flow2 先接受 f, g 兩個函數,之後回傳一個接受 x 變數後會傳回運算結果的函數。
這邊需要注意型別的部分,在放入 x 的時候會先被 f 運算,再經由 g 運算,型別會是經由以下變化。

"input  f output"
		  "input  g output"
number => number => string
   x       f(x)     g(f(x))

所以在組合函式的時候需要注意相鄰兩個函式的之間的型別是否相同才能組合,但剛剛做的 flow2 只能接受 number => number => string ,這樣組合的侷限性太大了,接下來以 Generic 來重新製作一個組合函式。

type Flow2 = <A, B, C>(fn1: (x: A) => B, fn2: (x: B) => C) => (x: A) => C;
const flow2: Flow2 = (fn1, fn2) => x => fn2(fn1(x));

list.map(flow2(inc, toString)); // ['2', '3', '4', '5', '6']
list.map(flow2(toString, split)); // [['1'], ['2'], ['3'], ['4'], ['5']]

如此一來就可以製作型別安全的 flow 了,最後稍微調整一下來製作 pipe

type Pipe2 <A, B, C>(x: A, fn1: (x: A) => B, fn2: (x: B) => C) => C;
const pipe2: Pipe2 = (x, fn1, fn2) => (fn2, fn1(x));

// usage
pipe(1, inc, toString) // '2'

今天的主題在 should-i-use-fp-tssrc/day-06 並且有習題和測試可以讓大家練習。

Reference

Functional Programming - 04: Function Composition


上一篇
[Should I use fp-ts?] Day 05 - pointfree
下一篇
[Should I use fp-ts?] Day 07 - fp-ts: Option 型別以及建構子
系列文
Should I use fp-ts25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言