前一篇文章我们谈到了如何实作一个 Maybe
Monad,而其主要的功能就是处理无值的情境,今天我们要来介绍另外一种写法去实作 Maybe
Monad。
如果有用 rxjs 或是 fp-ts 之类的函式库,可能会很熟悉这种写法
pipe(1, R.add(2), R.add(3)) // 6
没错,在 rxjs v6 之後都改用 pipe 的方式执行操作符, fp-ts 也是以 pipe 的方式为主,那相较於传统的链式写法,用 pipe 的方式写有什麽优点呢?
笔者认为是
那废话不多说,开始改写吧!
在这之前都假设各位读者都知道什麽是 pattern match 了!
在这之前笔者参考 fp-ts 的命名,在 fp-ts 其 Maybe Monad 称为 option,不同於 Maybe 的 Nothing
跟 Just
, option 是用 none
表示无值, some
表示有值。
然而我们是用 pipe 执行,那就需要先有该函式,来实作一个吧
实作 pipe
const pipe = (init, ...fns) =>
fns.reduce((prevValue, fn) => fn(prevValue), init);
跟 Function Composition 那章提到得概念一样, 这次我们只是将我们要执行的初始值放在第一个,接下来再放入我们要执行 piping 的函式。
Constructor
Option a = some a | none
const none = {_tag: "None"}
const some = value => ({_tag: "Some", value})
const match = (onNone, onSome) => (fa) => {
switch (fa._tag) {
case 'None':
return onNone;
case 'Some':
return onSome(fa.value);
default:
break;
}
};
const of = (x) => some(x);
Functor
const map = (g) =>
match(
() => none,
(b) => some(g(b))
);
pipe(of(10), map(R.add(2))) // some(12)
pipe(none, map(R.add(2))) // none
map
就是将目前 option 的状态 (None || Some) 去辨别在 match 时是要执行 onNone
还是 onSome
,而概念都跟前一章提到得一样,就不在此赘述了。
Applicative Functor
const ap = (f1) => (f2) =>
pipe(
f2,
match(
() => none,
(f) =>
pipe(
f1,
match(
() => none,
(a) => some(f(a))
)
)
)
);
const lift2 = R.curry((g, f1, f2) => pipe(f1, map(g), ap(f2)));
lift2(R.add, of(2), of(2)) // some(4)
lift2(R.add, of(2), none) // none
这边就稍微复杂了一点,以范例来讲,由於 ap
是 curried 函式,所以当我们放入 ap(some(2))
的时候不会立即执行,而是会等到 piping 之後的 some(R.add(2))
传入函式才会被执行,也就是 ap
函式参数 f2
,以此例子来说, f2
是 some(R.add(2))
并不是 none
, match 函式就会 callback onSome
,接下来 f1
是 some(2)
所以 match
也会 callback 执行 onSome
,最终就会回传 some(4)
。
Chain
const chain = (g) => match(() => none, g);
const safeHead = xs => xs.length === 0 ? none : some(xs[0])
pipe(of([1, 2, 3]), chain(safeHead)) // some(1)
pipe(of([]), chain(safeHead)) // none
chain 也跟前一章提到的一样,就是打平,而跟 map
再 join
的方式不同,在执行的时候就不会包着 x => some(g(x))
而是直接执行该函式。
改写前篇 chain
的写法就会变成,而概念也是一样的
class Maybe {
...
chain(f) {
return f(this.val)
}
}
Option
const option = (dv) => match(dv, R.identity);
pipe(
of([1, 2, 3]),
chain(safeHead),
option([])
); // 1
pipe(
of([]),
chain(safeHead),
option([])
); // []
而 option 就是把值真正取出来,所以如果最终是有值的情况,我们就是用 identity 函式取值出来,空值则是回传 default value.
不知道大家觉得哪种写法比较赞呢? dot chain 还是 pipe?
感谢大家阅读
Either Monad
<<: [Day 20] Edge Impulse + BLE Sense实现唤醒词辨识(上)
上传档案後制作超连结下载档案 - 用innerHTML制作超连结 code 最後就是如何显示map...
今日目标 学习了解 Python Pandas 的观念与运用 What is Pandas? Pan...
我第一次听过程序竞赛时是在我刚进到国三的下学期。 在这之前我对於程序的相关经验除了国中科展学的 PH...
如何载入CND 1.Fork 2.齿轮 ...
不怎麽重要的前言 上一篇介绍了while loop的概念,让大家在回圈的使用上可以相对的弹性。 这次...