跳到主要内容

变型函子

Bifunctor 怎样同时映射两个位置?​

  • Bifunctor: 拥有两个独立类型参数,可分别变换两个位置;
  • bimap(f, g): f 处理第一个位置,g 处理第二个位置;
const Pair = (first, second) => ({
first,
second,
bimap: (f, g) => Pair(f(first), g(second)),
});

Pair("alice", 10).bimap(
(name) => name.toUpperCase(),
(score) => score * 2,
); // Pair('ALICE', 20)

Contravariant Functor 为什么反向变换输入?​

  • 普通协变函子: 用 A -> B 把生产者 F<A> 变成 F<B>;
  • 逆变函子: 用 B -> A 预处理消费者的输入,把 F<A> 适配为 F<B>;
  • 场景: 谓词、验证器、编码器和排序比较器;
const Predicate = (test) => ({
test,
contramap: (fn) => Predicate((value) => test(fn(value))),
});

const isLong = Predicate((text) => text.length > 5);
const hasLongBio = isLong.contramap((user) => user.bio);

hasLongBio.test({ bio: "Hello World" }); // true

Profunctor 怎样同时改造输入与输出?​

  • Profunctor P<A, B>: 消费 A 并生产 B;
  • 第一个位置逆变: 用 A' -> A 预处理输入;
  • 第二个位置协变: 用 B -> B' 后处理输出;
const Profunctor = (run) => ({
run,
promap: (before, after) => Profunctor((value) => after(run(before(value)))),
});

const stringLength = Profunctor((text) => text.length);
const isTrimmedLengthEven = stringLength.promap(
(raw) => raw.trim(),
(length) => length % 2 === 0,
);

isTrimmedLengthEven.run(" code "); // true

三种结构的方向怎样比较?​

结构变换方向
Bifunctor两个位置都协变
Contravariant Functor消费者输入位置逆变
Profunctor输入逆变、输出协变