跳到主要内容

单子与余单子

单子的 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从上下文观察值并扩展观察结果