點燈坊

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

使用 chain 將 Resolved 與回傳 Future 的 Function 綁定

Sam Xiao's Avatar 2021-07-22

若要以 Function Pipeline 處理 Resolved,可使用多個 chain,如同 Promise 可使用多個 then,唯 chain 只接受回傳 Future 的 Function。

Version

Fluture 14.0.0

chain

import { create, env } from 'sanctuary'
import { resolve, fork, map, chain } 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 = resolve (1)

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

data 為 Resolved,mapchain 都能在 Function Pipeline 處理 Resolved:

  • map:僅接受一般 function
  • chain:僅接受回傳 Future 的 function

chain :: Chain m => (a -> m b) -> m a -> m b
將 Resolved 與回傳 Future 的 function 綁定並回傳新 Future

a -> m b:回傳 Future 的 function

m a:data 為 Chain

m b:回傳新 Chain

Future 亦為 Chain,所以也可使用 chain

chain000

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

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

let data = resolve (1)

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

亦可使用 Sanctuary 的 chain,因為 Sanctuary 與 Fluture 皆支援 Fantasy Land 的 Chain。

chain004

Function Pipeline

import { create, env } from 'sanctuary'
import { resolve, reject, fork, map, chain } 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 = resolve (1)

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

chain 最可貴的是回傳 Future,這讓 Future 也能使用多個 chain 完成 Function Pipeline,如同 Promise 使用多個 then 一樣。

chain 可能回傳 Resolved,亦可能回傳 Rejected,因此可能繼續留在 Resolved Track,也可能切到 Rejected Track。

chain001

Point-free

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

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

let data = resolve (1)

pipe ([
  map (add (1)),
  chain (encase (add (2))),
  chain (compose (reject) (negate)),
  fork (error) (log)
]) (data) 

也可使 mapchain 的 callback 能 Point-free。

chain002

Railway Oriented Programming

chain003

chain 支援 Railway Oriented Programming,因為傳入回傳 Future 的 function,可能是 Rejected 或 Resolved,因此可能切到 Rejected Track,或者繼續在 Resolved Track。

Conclusion

  • 亦可使用 Sanctuary 的 chain 取代 Fluture 的 chain,因為 Sanctuary 與 Fluture 都支援 Chain
  • 若要使用 Sanctuary 的 chain,會出現 type checking 錯誤,因為 Sanctuary 不認識 Future,可取消 type checking,或者加入 Future 為 Sanctuary 定義的 fluture-sanctuary-types 使其認識 Future
  • chain 在實務上會搭配相依性 API,第一個 API 回傳 Future 後,第二個 API 再傳入 chain

Reference

Aldwin Vlasbolm, Functional Alternative to Promises
Fluture, chain