x
想象一下,我们想对当且仅当x
非空时应用一系列函数:
if (x !== null) x = a(x);
if (x !== null) x = b(x);
if (x !== null) x = c(x);
现在假设我们需要对以下内容做同样的事情y
:
if (y !== null) y = a(y);
if (y !== null) y = b(y);
if (y !== null) y = c(y);
和同样的z
:
if (z !== null) z = a(z);
if (z !== null) z = b(z);
if (z !== null) z = c(z);
如您所见,如果没有适当的抽象,我们最终会一遍又一遍地重复代码。这样的抽象已经存在:Maybe monad。
Maybe monad 拥有一个值和一个计算上下文:
- monad 保持值安全并对其应用函数。
- 计算上下文是应用函数之前的空检查。
一个简单的实现应该是这样的:
⚠️ 此实现仅用于说明目的!这不是应该做的,而且在很多层面上都是错误的。但是,这应该让您更好地了解我在说什么。
如您所见,没有什么可以破坏:
- 我们将一系列功能应用于我们的价值
- 如果在任何时候,值变为空(或未定义),我们就不再应用任何函数。
const abc = obj =>
Maybe
.of(obj)
.map(o => o.a)
.map(o => o.b)
.map(o => o.c)
.value;
const values = [
{},
{a: {}},
{a: {b: {}}},
{a: {b: {c: 42}}}
];
console.log(
values.map(abc)
);
<script>
function Maybe(x) {
this.value = x; //-> container for our value
}
Maybe.of = x => new Maybe(x);
Maybe.prototype.map = function (fn) {
if (this.value == null) { //-> computational context
return this;
}
return Maybe.of(fn(this.value));
};
</script>
附录1
我无法解释 monad 是什么,因为这不是这篇文章的目的,而且还有比我更擅长这方面的人。然而,正如 Eric Elliot 在他的博客文章JavaScript Monads Made Simple中所说:
无论您的技能水平或对类别理论的理解如何,使用 monad 都会使您的代码更易于使用。未能利用 monad 可能会使您的代码更难处理(例如,回调地狱、嵌套条件分支、更冗长)。
附录二
以下是我如何使用来自monetjs的Maybe monad解决您的问题
const prop = key => obj => Maybe.fromNull(obj[key]);
const abc = obj =>
Maybe
.fromNull(obj)
.flatMap(prop('a'))
.flatMap(prop('b'))
.flatMap(prop('c'))
.orSome('')
const values = [
{},
{a: {}},
{a: {b: {}}},
{a: {b: {c: 42}}}
];
console.log(
values.map(abc)
);
<script src="https://www.unpkg.com/monet@0.9.0/dist/monet.js"></script>
<script>const {Maybe} = Monet;</script>