點燈坊

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

使用 ap 陸續提供 Future 給回傳 Future 的 Function

Sam Xiao's Avatar 2021-07-27

Fluture 也提供 ap,是以 Function 為主體,跟 Sanctuary 以 Data 為主體的考量不太一樣,其 Type Signature 剛好相反。

Version

Fluture 14.0.0

ap

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

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

let data1 = resolve (1)
let data2 = resolve (2)

let add_ = resolve (add)

pipe ([
  ap (data1),
  ap (data2),
  fork (error) (log)
]) (add_)

add_ 為包進 Future 的 binary function,將 function 傳進 ap 後,可不斷地呼叫 ap 將 Future 補齊後執行 function。

Future 的 ap 最後一個 argument 為包進 Future 的 function。

ap :: Apply m => m a -> m (a -> b) -> m b
陸續提供 Future 將回傳 Future 的 Function 綁定

  • m a:data 為 Apply
  • m (a -> b):包進 Apply 的 function
  • m b:回傳新 Apply

ap000

reject

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

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

let data1 = resolve (1)
let data2 = reject (2)

let add_ = resolve (add)

pipe ([
  ap (data1),
  ap (data2),
  fork (error) (log)
]) (add_)

第 8 行

let data1 = resolve (1)
let data2 = reject (2)

data1data2 都是 Future,但 data1 為 Resolved,而 data2 為 Rejected。

13 行

pipe ([
  ap (data1),
  ap (data2),
  fork (error) (log)
]) (add_)

用法完全相同,但結果會是 Rejected。

ap001

Sanctuary

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

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

let add_ = resolve (add (1))

let data = resolve (1)

pipe ([
  ap (add_),
  fork (error) (log)
]) (data)

Sanctuary 也有 ap,但其最後一個 argument 為 Future,第一個 argument 才是包進 Future 的 function,也就是 Sanctuary 與 Future 的 ap 的 type signature 剛好相反。

Apply f => f (a -> b) -> f a -> f b
將 Apply 與包進 Apply 的 function 綁定並回傳新 Apply

Consider flipping the argument order of ap#645 中,Future 的原作者 Aldwin Viasblom 與 Sanctuary 原作者 David Chambers 也曾討論過此問題,事實上 Sanctuary 的 ap 與 Fantasy Land 的 type signature 剛好相反,Future 與 Ramda 的 ap 才是遵循 Fantasy Land。

但 Fantasy Land 的 ap 又與 Haskell、F# 的 ap 相反,所以 Sanctuary 的堅持也是有它的道理。

只能說 Sanctuary 的 ap 對 data 較友善,而 Future 與 Fantasy Land 的 ap 對 function 較友善,各有其優點,ap 也是唯一 Sanctuary 沒有遵循 Fantasy Land 的 function。

ap002

flip

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

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

let add_ = resolve (add (1))

let data = resolve (1)

pipe ([
  flip (ap) (add_),
  fork (error) (log)
]) (data)

12 行

pipe ([
  flip (ap) (add_),
  fork (error) (log)
]) (data)

若要使用 Future 的 ap 實現 Sanctuary 的 ap 也不難,只要將其 flip 即可。

ap003

Conclusion

  • Fluture 的 ap 與 Sanctuary 的 ap 功能完全一樣,也都支持 Apply,唯兩者 type signature 剛好相反,Fluture 的 flip (ap) 剛好等於 Sanctuary 的 ap
  • 事實上 Fluture 的 ap 才是真正支援 Fantasy Land,但 Sanctuary 的 ap 則與 F# 與 Haskell 相同

Reference

Fluture, ap
Sanctuary, Consider flipping the argument order of ap#645