點燈坊

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

使用 mapRej 將 Rejected 與一般 Function 綁定

Sam Xiao's Avatar 2021-07-22

若要以 Function Pipeline 處理 Rejected,可使用多個 mapRej,不像 Promise 只能使用一個 otherwise

Version

Fluture 14.0.0

mapRej

import { create, env } from 'sanctuary'
import { reject, fork, map, mapRej } from 'fluture'
import { env as flutureEnv } from 'fluture-sanctuary-types'
import { error, log } from 'wink-fp'

let { pipe } = create ({ checkTypes: true, env: env.concat (flutureEnv) })

let data = reject (1)

pipe ([
  map (x => x + 1),
  mapRej (x => x + 2),
  fork (error) (log)
]) (data) 

data 為 Rejected,儘管在 pipe 中同時有處理 Resolved 的 map,亦有處理 Rejected 的 mapRej,整個 Function Pipeline 會無視 map,只執行 mapRej

mapRej :: (a -> c) -> Future a b -> Future c b
將 Rejected 與一般 function 綁定並回傳新 Rejected

a -> c:一般 function

Future a b:data 為 Future

Future c b:回傳新 Future

mapRej 為 Future 專屬 function,因此只支援 Future,不像 map 是支援 Functor

maprej000

Function Pipeline

import { create, env } from 'sanctuary'
import { reject, fork, map, mapRej } from 'fluture'
import { env as flutureEnv } from 'fluture-sanctuary-types'
import { error, log } from 'wink-fp'

let { pipe } = create ({ checkTypes: true, env: env.concat (flutureEnv) })

let data = reject (1)

pipe ([
  map (x => x + 1),
  mapRej (x => x + 2),
  mapRej (x => -x),
  fork (error) (log)
]) (data) 

mapRej 最可貴的是回傳 Rejected,這讓 Rejected 也能使用多個 mapRej 完成 Function Pipeline。

Ramda 的 otherwise 會回傳 Resolved,這使得整個 Function Pipeline 中只能使用一個 otherwise,若要使用 Function Pipline 處理 Rejected,只能另外抽 function 再傳入 otherwise,而不能如 Future 一樣在一個 Function Pipeline 同時處理 Resolved 與 Rejected。

maprej001

Point-free

import { create, env } from 'sanctuary'
import { reject, fork, map, mapRej } from 'fluture'
import { env as flutureEnv } from 'fluture-sanctuary-types'
import { error, log } from 'wink-fp'

let { pipe, add, negate } = create ({ checkTypes: true, env: env.concat (flutureEnv) })

let data = reject (1)

pipe ([
  map (add (1)),
  mapRej (add (2)),
  mapRej (negate),
  fork (error) (log)
]) (data) 

mapmapRej 的 callback 可傳入 addnegate 使其 Point-free。

maprej002

Composition Law

import { create, env } from 'sanctuary'
import { reject, fork, map, mapRej } from 'fluture'
import { env as flutureEnv } from 'fluture-sanctuary-types'
import { error, log } from 'wink-fp'

let { pipe, add, negate } = create ({ checkTypes: true, env: env.concat (flutureEnv) })

let data = reject (1)

let t = pipe ([
  add (2),
  negate
])

pipe ([
  map (add (1)),
  mapRej (t),
  fork (error) (log)
]) (data)

Future 也是 Functor,因此支援 composition law,可將 mapRej 的 callback 都以 pipe 組合成 t 再一次傳入 map

maprej003

Railway Oriented Programming

maprej004

mapRej 支援 Railway Oriented Programming,因為傳入一般 function,回傳仍然是 Rejected Future,因此繼續留在 Rejected Track,不會切到 Resolved Track。

Conclusion

  • Promise 只能在同一個 Function Pipeline 中使用一次 otherwise,而 Future 可使用多次 mapRej,這使得 Future 可在同一個 Function Pipeline 中處理 Rejected Future 與 Resolved Future

Reference

Aldwin Vlasbolm, Functional Alternative to Promises
Fluture, mapRej