单子与余单子
单子的 chain 比函子的 map 多做什么?
- 单子: 来源以具有
of与chain的对象描述; map: 返回嵌套上下文,例如F<F<B>>;chain: 应用返回同类单子的函数,并把一层嵌套压平;- 别名:
of也称return;chain也称flatMap或bind;
const chain = (xs, fn) => xs.flatMap(fn);
chain(["cat,dog", "fish,bird"], (text) => text.split(","))[
// ['cat', 'dog', 'fish', 'bird']
("cat,dog", "fish,bird")
].map((text) => text.split(","));
// [['cat', 'dog'], ['fish', 'bird']]
Kleisli 组合怎样连接返回单子的函数?
- Kleisli 箭头: 形如
A -> M<B>的函数; - 可组合条件: 两个函数返回同类单子,且前一结果类型匹配后一参数;
- 实现: 先执行前一函数,再用
chain执行后一函数;
const kleisliCompose = (g, f) => (x) => f(x).chain(g);
const safeParse = (text) => {
const number = Number(text);
return Number.isNaN(number) ? None() : Some(number);
};
const positive = (number) => (number > 0 ? Some(number) : None());
const parsePositive = kleisliCompose(positive, safeParse);
parsePositive("1"); // Some(1)
parsePositive("bad"); // None()
余单子怎样从上下文中读取并扩展?
- 余单子(Comonad): 提供
extract与extend; extract: 从上下文取出当前值;extend: 把观察整个余单子的函数用于上下文,再得到同类余单子;
const CoIdentity = (value) => ({
extract: () => value,
extend: (fn) => CoIdentity(fn(CoIdentity(value))),
});
CoIdentity(1).extract(); // 1
CoIdentity(1)
.extend((co) => co.extract() + 1)
.extract(); // 2
单子与余单子的方向有什么不同?
| 结构 | 核心操作 | 直观方向 |
|---|---|---|
| 单子 | chain | 把值送入产生上下文的下一步 |
| 余单子 | extract | 从上下文观察值并扩展观察结果 |