函子与单子
函子(Functor)和单子(Monad)是函数式编程中的抽象概念,它们为处理容器类型提供了统一的接口。
什么是函子?
函子是实现了 map 方法的容器类型,它允许你将一个函数应用到容器中的值。
// 函子接口
class Functor {
map(fn) {
// 将函数 fn 应用于容器中的值
}
}
函子定律
- 同一律:
functor.map(x => x)等于functor - 组合律:
functor.map(x => f(g(x)))等于functor.map(g).map(f)
简单函子实现
class Container {
constructor(value) {
this.value = value;
}
static of(value) {
return new Container(value);
}
map(fn) {
return Container.of(fn(this.value));
}
}
// 使用
Container.of(5)
.map(x => x + 1)
.map(x => x * 2);
// Container { value: 12 }
什么是单子?
单子(Monad)是函子的扩展,它增加了 flatMap(或 chain、bind)方法,用于处理嵌套容器。
// 单子接口
class Monad {
map(fn) { /* ... */ }
flatMap(fn) { /* ... */ }
}
单子定律
- 左同一律:
Monad.of(x).flatMap(f)等于f(x) - 右同一律:
m.flatMap(Monad.of)等于m - 结合律:
m.flatMap(f).flatMap(g)等于m.flatMap(x => f(x).flatMap(g))
Maybe 单子
class Maybe {
static just(value) {
return new Just(value);
}
static nothing() {
return new Nothing();
}
static of(value) {
return value == null ? new Nothing() : new Just(value);
}
}
class Just extends Maybe {
constructor(value) {
super();
this.value = value;
}
map(fn) {
return Maybe.just(fn(this.value));
}
flatMap(fn) {
return fn(this.value);
}
getOrElse(_) {
return this.value;
}
}
class Nothing extends Maybe {
map(_) {
return this;
}
flatMap(_) {
return this;
}
getOrElse(defaultValue) {
return defaultValue;
}
}
实际应用
处理可能为空的值
const user = {
profile: {
address: {
city: 'New York'
}
}
};
// 命令式风格
const city = user && user.profile && user.profile.address && user.profile.address.city;
// 使用 Maybe
const getCity = (user) =>
Maybe.of(user)
.flatMap(u => Maybe.of(u.profile))
.flatMap(p => Maybe.of(p.address))
.map(a => a.city)
.getOrElse('Unknown');
Promise 也是单子
// Promise 实现了单子接口
Promise.resolve(5)
.then(x => x + 1) // map
.then(x => Promise.resolve(x * 2)) // flatMap
.then(console.log); // 12
数组作为函子
// 数组是函子
[1, 2, 3].map(x => x * 2); // [2, 4, 6]
// 数组也是单子(通过 flatMap)
[1, 2, 3].flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]
总结
- 函子:提供
map方法,用于转换容器中的值 - 单子:扩展函子,提供
flatMap方法,用于展平嵌套容器 - 优势:统一接口、链式操作、处理副作用