點燈坊

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

使用 ap 將 Apply 與包進 Apply 的 Function 綁定

Sam Xiao's Avatar 2021-07-08

支援 ap 的 Object 稱為 Apply,可使用 ap 將 Apply 與回傳 Apply 的 Function 綁定改變 Apply。

Version

Ramda 0.27.1

Array

import { ap, inc } from 'ramda'

let data = [1, 2]

ap ([inc]) (data) // ?

data 為 Array,inc 為包進 Array 的 function,可使用 ap 將 Array 與 [inc] 綁定改變 Array。

ap
[a → b] → [a] → [b]
將 Array 與包進 Array 的 function 綁定改變 Array

[a -> b]:包進 Array 的 function

[a]:data 為 Array

[b]:回傳新 Array

ap000

import { ap, inc, negate } from 'ramda'

let data = [1, 2]

ap ([inc, negate]) (data) // ?

也可將多個 function 包在 Array 內,若 datam 的 element,傳入有 n 個 function,則 ap 會產生 m x n element 的新 Array。

ap001

Object

import { ap, inc, negate } from 'ramda'

let data = { x: 1, y: 2 }

ap ({ x: inc, y: negate }) (data) // ?

data 為 Object,想傳入 incnegate 改變 data,理論上可將 incnegate 也包進 Object 再傳入 ap,function 只會影響相同 key 的 property。

此在 Sanctuary 可正常執行,可惜在 Ramda 無法使用。

ap002

Maybe

Just

import { ap, inc, negate } from 'ramda'
import { Just } from 'sanctuary'

let data = Just (1)

let inc_ = Just (inc)

ap (inc_) (data) // ?

data 為 Maybe,inc_ 為包進 Maybe 的 function,可使用 ap 將 Maybe 與 inc_ 綁定改變 Maybe。

ap
Apply f => f (a → b) → f a → f b
將 Apply 與包進 Apply 的 function 綁定改變 Apply

f (a -> b):包進 Apply 的 function

f a:data 為 Apply

f b:回傳新 Apply

Data 與 function 必須包在相同 typeclass 的 Object 內,因此都必須是 Maybe

ap003

Nothing

import { ap, inc, negate } from 'ramda'
import { Just, Nothing } from 'sanctuary'

let data = Nothing

let inc_ = Just (inc)

ap (inc_) (data) // ?

data 為 Nothing,則傳入 ap 的 function 不會執行。

ap004

Either

import { ap, inc } from 'ramda'
import { Right } from 'sanctuary'

let data = Right (1)

let inc_ = Right (inc)

ap (inc_) (data) // ?

data 為 Either,inc_ 為包進 Either 的 function,可使用 ap 將 Either 與 inc_ 綁定改變 Either。

Data 與 function 必須包在相同 typeclass 的 Object 內,因此都必須是 Either

ap005

Left

import { ap, inc } from 'ramda'
import { Left, Right } from 'sanctuary'

let data = Left (1)

let inc_ = Right (inc)

ap (inc_) (data) // ?

data 為 Left,則傳入 ap 的 function 不會執行。

ap006

Future

Resolved

import { resolve, fork } from 'fluture'
import { pipe, ap, inc } from 'ramda'
import { log, error } from 'wink-fp'

let data = resolve (1)

let inc_ = resolve (inc)

pipe (
  ap (inc_),
  fork (error) (log)
) (data) 

data 為 Future,inc_ 為包進 Future 的 function,可使用 ap 將 Future 與 inc_ 綁定改變 Future。

Data 與 function 必須包在相同 typeclass 的 Object 內,因此都必須是 Future

ap007

Rejected

import { resolve, reject, fork } from 'fluture'
import { pipe, ap, inc } from 'ramda'
import { log, error } from 'wink-fp'

let data = reject (1)

let inc_ = (resolve) (inc)

pipe (
  ap (inc_),
  fork (error) (log)
) (data)

data 為 Rejected,則傳入 ap 的 function 不會執行。

ap008

Conclusion

  • 支援 ap 的 Object 稱為 Apply
  • 可發現 Functor f => map ((a -> b)) -> f aApply f => ap (f (a -> b)) -> f a 是等效的,只是以不同方式處理 function,Apply 要透過 ap 傳入包進 Apply 的 function;而 Functor 則透過 map 直接傳入 function 即可
  • ap 除了能用在 Array 外,也能用在 Maybe、Either 與 Future,因為這些也都是 Apply,可惜 Ramda 的 ap 無法用在 Object

Reference

Ramda, ap