Day 16 - Apply

到目前为止我们已经知道了 Functor 可以将 effect 跟 pure function 进行 compose,但我们没有提到一点是 Functor 无法应付的,而今天要介绍的主角就是用来解决此一情境的,Apply

f g composition
pure pure compose(f, g)
effects pure(unary) f.map(g)
effects pure(n-ary) ?

举例来说,现在有两个 Identity,分别是 Identity(1)Identity(2),我们要如何将其进行相加

如果用一般正常函式的相加,看来是行不通

const add = (x, y) => x + y;

add(Identity.of(1), Identity.of(2)) // [object Object][object Object]

可能有读者想到, 如果用 map 将分别将 Identity 内的值取出,再将 add 在套用到取出後的值就好了

const result = Identity.of(1).map(x => Identity.of(2).map(y => add(x, y))) // Identity(Identity(3))

确实,这样也是一个办法,但这种方法不但会导致前後相依,也就是必须等待前面的运算完成後才能进行下一个运算,也会使运算後变成深层结构 Identity(Identity(3)),取值时必须

result.val.val

那该怎麽办呢? 有没有方法让其既可以是独立的也可以维持单层结构,就让来介绍 Apply 吧!

Apply

Type Signature

ap :: Apply f => f a ~> f (a -> b) -> f b

Law

  • Composition: v.ap(u.ap(a.map(f => g => x => f(g(x))))) === v.ap(u).ap(a)

Implementation

所以要解决上述提到的问题可以分成四步骤

1. 将 add 函式进行 currying

const add = R.curry((x, y) => x + y);

2. 实作 ap

const Identity = (val) => ({
  val,
  map: (f) => {
    const result = f(val);
    return Identity(result); 
  },
  ap: function (o) {
    // 根据 Fantasy-land 对 Apply 定义,其为 `a.ap(b)` 其 `b` 一定会是函式,而在此范例则是 o.val 必为函式
    return this.map(o.val);
  },
  inspect: () => `Identity(${val})`,
});

3. 建立一个 helper function lift2

const lift2 = g => f1 => f2 => f2.ap(f1.map(g));

注意: 这就为什麽我们的 g 一定要进行 currying

而其实 ap 就是将两个 Container 进行合并,相较於前面章节提过的 Semigroup 则是透过呼叫 concat进行合并。

另外如果今天是要将 g (3-ary) 合并三个 Container

const lift3 = g => f1 => f2 => f3 => f3.ap(f2.ap(f1.map(g)));

4. 接下来就可以轻松的解决一开始的问题

lift2(add)(Identity(1))(Identity(2)) // Identity(3)

小结

没错,我们现在解决了 effects 跟 pure(n-ary) 的问题!!! 所以来更新一下一开始提到的表吧!

f g composition
pure pure compose(f, g)
effects pure(unary) f.map(g)
effects pure(n-ary) f1.map(g).ap(f2)

感谢大家的阅读!!

Reference

  1. Apply

<<:  Day 0x15 - 代码建立 (Part 1: 回覆讯息代码)

>>:  30天零负担轻松学会制作APP介面及设计【DAY 22】

[Day 13] - 初探永丰银行线上收款API - 丰收款 - 取得Nonce

取得Nonce 根据规格书的说明,每次呼叫api前都要先取得Nonce 要取得Nonce也满简单的,...

[Day 11] Reactive Programming - Reactor(Scheduler)

前言 Reactor 是concurrency-agnostic ,花了一点时间研究这个英文单字的意...

IT铁人DAY 13-Composite 组合模式

  今天要来介绍Composite Pattern,是属於Structural Design Pat...

Day 29 | GitHub Pages 初体验

今天想分享一下如何上传自己的 GitHub Pages, 可以参考高见龙的 使用 GitHub 免费...

Azure AutoML02及结语

AutoML得到的结果,说明如下。见图<AZ-exp4MNIST.png> 当看到 [S...