前言
从先前 Functor 的概念出发,通过把数值包裹在容器促进函数组合如下:
const addThree = (x) => x + 3const multiplyByTwo = (x) => x * 2
Maybe(2).map(addThree); // Maybe(5).map(multiplyByTwo); // Maybe(10)但如果函数本身也被包在容器中呢?
const maybeAddThree = Maybe(addThree)Functor 的 map 只能作用在数值上,而不能是「存在于容器中的数值」上,所以才需要 Applicative Functor。
什么是 Applicative Functor?
一种比 Functor 更多功能的结构,除了满足 Functor 的功能外还能「让盒子里的函数作用(apply)到另一盒子的值」。
function Maybe(value) { return value == null ? Nothing() : Just(value);}
function Just(value) { return { map: f => Maybe(f(value)), apply: f => f.map(value), getOrElse: () => value, };}
function Nothing() { return { map: () => Nothing(), apply: () => Nothing(), getOrElse: defaultValue => defaultValue, };}
const add = a => b => a + b;
Maybe(add).apply(Maybe(1)) // Maybe(1 + b).apply(Maybe(1)) // Maybe(1 + 1).getOrElse(0) // 2以上 Applicative Functor 达成在不拆解包装的情况下进行运算。
我还是不懂 Applicative Functor 的应用场合
假设要验证用户「输入名称」与「Email」且转小写与大写,每个验证都可能失败,使用 Functor 会写成这样:
const validateName = name => name ? Just(name) : Nothing();
const validateEmail = email => email.includes('@') ? Just(email) : Nothing();
const nameResult = validateName("webdong") .map(name => name.toUpperCase()) .getOrElse("Invalid name")
.map(email => email.toLowerCase()) .getOrElse("Invalid email")
const makeUser = name => email => ({ name, email });
console.log(makeUser(nameResult)(emailResult))多个 Functor 处理起来会是个问题,或是在 Functor 中处理其他 Functor 也是个问题:
// Just(Just({ name, email }))validateName("Rice") .map(name => .map(email => makeUser(name)(email)) );有了 Applicative Functor 就可以轻松组合多个 Functor:
const validateName = name => name ? Maybe(name) : Nothing();
const validateEmail = email => email.includes('@') ? Maybe(email) : Nothing();
const makeUser = name => email => ({ name, email });
Maybe(makeUser) .apply(validateName("webdong"))// → Maybe({ name: "Rice", email: "[email protected]" })
Maybe(makeUser) .apply(validateName(""))// → Nothing或是将 Promise 视为 Applicative Functor 的视角来看:
Promise.resolve(add) .then(f => Promise.all([Promise.resolve(2), Promise.resolve(3)]) .then(([a, b]) => f(a)(b))) .then(console.log); // 5Applicative Functor 定义
Applicative Functor 可以被视为 Functor 的进阶版。 Functor 只能「让函数作用在容器里的值上」, 而 Applicative 则进一步允许「容器里的函数」作用在「另一个容器里的值」上,要实现 Applicative 需要实践两种方法:
of:把一般值包装进容器。apply或ap:让「装在容器里的函数」作用到「装在容器里的值」。
- Identity
// 用一个「不改变内容」的函数包起来再套用,结果应与原值相同。// 例如:Maybe.of(x => x).apply(Maybe(5)) === Maybe(5)A.of(x => x).apply(v) === v- Homomorphism
// 直接应用函数与把函数和值都包起来后再套用,结果应一致。// 例如:Maybe.of(f).apply(Maybe.of(x)) === Maybe.of(f(x))A.of(f).apply(A.of(x)) === A.of(f(x))- Interchange
// 套用的顺序可互换,只要函数应用逻辑相同。// 例如:Maybe(fn).apply(Maybe.of(y)) === Maybe.of(f => f(y)).apply(Maybe(fn))u.apply(A.of(y)) === A.of(f => f(y)).apply(u)- Composition
// 套用多个函数时,应与它们的合成结果一致。// 例如: Maybe.of(compose).apply(u).apply(v).apply(w)// 等同于:u.apply(v.apply(w))A.of(compose).apply(u).apply(v).apply(w) === u.apply(v.apply(w))