點燈坊

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

使用 map 將 Resolved 與一般 Function 綁定

Sam Xiao's Avatar 2021-07-21

若要以 Function Pipeline 處理 Resolved,可使用多個 map,如同 Promise 可使用多個 then

map

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

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

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

map :: Functor m => (a -> b) -> m a -> m b
將 Resolved 與一般 function 綁定並回傳新 Resolved

a -> b:一般 function

m a:data 為 Functor

m b:回傳新 Functor

Future 亦為 Functor,所以也可使用 map

map000

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

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

let data =  resolve (1)

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

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

map005

Function Pipeline

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

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

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

map001

Point-free

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

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

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

map002

Composition Law

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

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

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

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

map003

Railway Oriented Programming

map004

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

Conclusion

  • 亦可使用 Sanctuary 的 map 取代 Fluture 的 map,因為 Sanctuary 與 Fluture 都支援 Functor
  • 若要使用 Sanctuary 的 map,會出現 type checking 錯誤,因為 Sanctuary 不認識 Future,可取消 type checking,或者加入 Future 為 Sanctuary 定義的 fluture-sanctuary-types 使其認識 Future

Reference

Aldwin Vlasbolm, Functional Alternative to Promises
Fluture, map