一本书里面内容较多, 因此分成了多篇 Post, 可以从此处看到相关文章:
实现 FP 需要保证一些函数的输入和输出规范化. 以方便 compose 进行链式调用.
例如: arr.map(x=>x+1).map(x=>x+2)
就是因为 .map()
方法的输入输出的规范化以使得其可以链式调用, 多个 map 方法可以进行 compose: const testMethod = compose(map(capitalize), map(getAscii));
原生的 Array 类型就是一个 Functor [1, 2, 3, 4].map(add).map(add)
F.map(x=>x) === F
f(g(x))
可以写成 f.map(g).map(x)
F.map((x) => f(g(x))) === F.map(g).map(f)
一般会实现以下属性和方法: 1. _value
: 通过这个属性从容器中取得实际的值 2. of()
: 给方法提供初始值 3. map()
: map 方法接收一个 fn, fn 去得到一个新的值
class Container {
static of(value) {
return new Container(value)
}
constructor(value) {
this._value = value
}
map(fn) {
return Container.of(fn(this._value))
}
}
let c = Container.of(5)
.map((x) => x * x)
.map((x) => x + 2)
console.log(c) // Container { _value: 27 }
const Functor = {
map(f = identity) {
/* 注意当前 Functor 需要用到 Container 的 get 方法 (因为 val 设置成了 private), 所有扩展了这个 Functor 的类都需要拥有这个 get 方法 */
return this.constructor.of(f(this.get()));
},
};
const double = (x) => x * 2;
const triple = (x) => x * 3;
class Container {
#val; /* 注意这个地方的 val 是 private, 因此必须使用 get 方法才能够得到对应的值. */
constructor(value) {
this.#val = value;
}
static of(value) {
return new Container(value);
}
get() {
return this.#val;
}
}
Object.assign(Container.prototype, Functor);
console.log(Container.of(2).get()); /* 2 */
console.log(Container.of(2).map(double).get()); /* 4 */
console.log(Container.of(2).map(double).map(triple).get()); /* 12 */
class Maybe {
static of(value) {
return new Maybe(value);
}
constructor(value) {
this._value = value;
}
map(fn) {
/* 在这个地方进行了一些特殊的处理 */
return this.isNothing() ? Maybe.of(null) : Maybe.of(fn(this._value));
}
isNothing() {
return this._value === null || this._value === undefined;
}
}
let obj = new Maybe(5)
.map((x) => null) /* 此处对应的值已经转化成了 null */
.map((x) => x * x);
console.log(obj);
number -> number
number -> number[]
number -> { res: number }
注意
chain
方法根据不同的实现可能会有不同的名称
M.of(a).chain(f) === f(a)
m.chain(M.of) === m
m.chain(f).chain(g) === m.chain(x => f(x).chain(g))
/* Number -> Number[], 如果连续调用将会得到多层嵌套数组 */
var f = (x) => [x ** 2];
var g = (x) => [x * 3];
/* Monad Container */
var Container = class {
of(value) {
return new Container(value);
}
constructor(value) {
this.value = value;
}
map(f) {
return new Container(f(this.value));
}
chain(f) {
/* 此处对于 Functor 的 map 进行了一些额外操作 */
/* 这个方法根据需求不同会有不同实现, 比如对一些 side effect 的处理. */
return new Container(f(this.value).flat()[0]);
}
};
var test = new Container(2);
// test.of(2).map(f).map(g); // 报错, Functor 的 map 方法无法处理数组
test.of(2).chain(f).chain(g); // Container { value: 12 }
const Functor = {
map(f = identity) {
return this.constructor.of(f(this.get()));
},
};
/* 将 Functor 扩展到 Monad 中 */
const Monad = Object.assign({}, Functor, {
flatMap(f) {
return this.map(f).get();
},
});
/* 如此一来 SomeClass 就同时拥有了 map 和 flatMap, SomeClass 就成为了一个 Monad */
Object.assign(SomeClass.prototype, Functor, Monad);