yo, what's up
在之前我们都是用 Identity 作为例子,但其功用并不大,所以今天要来开始介绍一些比较常用的 ADTs,今天就从 Maybe 开始进行介绍
Maybe Monad 是专门处理无值(
null
||undefined
) 情境的 Monad
其大部份的使用情境就是安全的取值,举例来说,当我们要透过 url param 的 key 去取 config 值後,再根据娶回来的资料渲染页面
// config.js
const pageConfig = {
"food": {
"imageUrl": "https://s3.image.com/food-example.png",
"foodMenu": [{"title": "fired chicked"}, {"title": "fired rice"}]
},
"travel": {
"imageUrl": "https://s3.image.com/travel-example.png",
"foodMenu": [{"title": "Taipei"}, {"title": "Tainan"}]
}
}
// app.js
const { parse } = require('query-string');
const paramsObj = parse(location.search)
此时空值的情况可能发生在 paramsObj
或是 pageConfig[paramsObj.target]
, 所以我们可能会有很多 if
在程序内
const getPageConfig = (target) => {
if(!target) return;
const config = pageConfig[target];
if(config) {
... // do something
}
}
接下来开始介绍 Maybe 吧!
Constructor
Maybe a = Just a | Nothing
Maybe
可以想像成是一个 Container,里面是由两种情境,有值 Just a
与空值 Nothing
组合而成的
class Maybe {
static of(val) {
return new Maybe(val);
}
get isNothing() {
return (this.val === null || this.val === undefined);
}
constructor(val) {
this.val = val;
}
map(fn) {
return this.isNothing ? this : Maybe.of(fn(this.val));
}
inspect() {
return this.isNothing ? 'Nothing' : `Just(${this.val})`;
}
}
而 Maybe
是一个 Functor, 而其可以与 pure function (unary) 进行 compose
Maybe.of(10)
.map(R.add(2))
.inspect() // Just(12)
Maybe.of(null)
.map(R.add(2))
.inspect() // Nothing
可以看到,当 Maybe a
其 a
值为 null
时,就不会继续运算下去,而直接跳过所有运算回传 Nothing
class Maybe {
...
ap(f) {
return this.isNothing ? this : this.map(f)
}
}
const lift2 = R.curry((g, f1, f2) => f2.ap(f1.map(g)))
而当 pure function 参数长度为 (n-ary),其概念也是跟之前 Identity 的逻辑很像,不同的是 Maybe 一样会判断如果为空值就会回传 Nothing
lift2(R.add, Maybe.of(2), Maybe.of(2)).inspect() // Just(4)
lift2(R.add, Maybe.of(2), Maybe.of(null)).inspect() // Nothing
class Maybe {
...
join() {
return this.isNothing ? this : this.val;
}
chain(f) {
return this.map(f).join()
}
}
如果要 compose 的函式是 effect,像是 safeHead
则我们就可以用 chain
去进行 compose
const safeHead = xs => Maybe.of(xs[0])
Maybe.of([1, 2, 3])
.chain(safeHead)
.inspect() // Just(1)
Maybe.of(null)
.chain(safeHead)
.inspect() // Nothing
Option
class Maybe {
...
option(defaultV) {
return this.isNothing ? defaultV : this.val;
}
}
到这里大家可能发现一个问题,要如何把 Container 里的值真正取出来,而 Option 的作用就是在此,并且可以放入一个 default value,当取出来的是 Nothing
的时候,Option 会将该值作为 fallback
Maybe.of(100)
.option('default value') // 100
Maybe.of(null)
.option('default value') // default value
solution
接下来就来解决我们一开始遇到的问题,在之前可以先 mock parse(location.search)
後的资料,其可能的值为 {target: 'xxx', ...}
或是 {...}
首先如果我们是正向的流程会是这样
const safeGet = R.curry((obj, target) => Maybe.of(obj[target]))
cosnt result = Maybe.of({target: "food"})
.map(R.prop('target'))
.chain(safeGet(pageConfig))
.option({})
// {
// "imageUrl": "https://s3.image.com/food-example.png",
// "foodMenu": [{"title": "fired chicked"}, {"title": "fired rice"}]
// }
若值为空,则是会 fallback 到 {}
cosnt result = Maybe.of({})
.map(R.prop('target'))
.chain(safeGet(pageConfig))
.option({}) // {}
感谢大家阅读,明天将介绍另外一种实作方式
NEXT: Maybe Monad II
NIST出版物 NIST制定并维护了大量有关信息和信息系统的安全性和隐私性的标准,指南,建议和研究。...
说到物件式储存,可能大家有用过的是AWS的S3、GCP的GCS,OSS (Object Stora...
You Need the “Exams4Success” Pegasystems PEGAPCSA8...
孩子兄弟标记法 记录 右侧索引(右边兄弟是谁),下层所引(孩子是谁) 完整树状转化 参考来源 大话资...
学习进度 设计模式 迭代器模式 观察者模式 Android Studio SQLite Room 心...