前言
我不是数学家也对范畴论没有太大兴趣,但它深刻的与Functional Programming进阶理念挂钩,透过实战应用了解开发层面这些理论可以如何撰写更好维护的程序。
Functor
想像成一个「装值的盒子」、「遵守特定规则的可映射结构」允许对内容进行运算,但同时又保留了盒子的结构,举例: 2 + 3 = 5
但如果数字被「包装」起来呢?例如:
[2]
(在数组里)Promise.resolve(2)
(在异步计算里)Maybe(2)
(可能有值,也可能没值)
这时候不能直接对 2
进行改动,但Functor提供了方式在容器里操作值通常被称作 map
。
[2].map(x => x + 3); // [5]
Promise.resolve(2).then(x => x + 3); // Promise(5)
Maybe(2).map(x => x + 3); // Maybe(5)
为什么需要Functor?
把数值包入「盒子」是为了让 数值存在上下文,举例制作一个 Maybe
Functor代表「可能存在、也可能不存在的值」,透过「盒子」内定义的规则根据输入返回不同「盒子」。
- 如果有值,就正常运算。
- 如果无值,就跳过运算返回
Nothing
。
function Maybe(value) { return value == null ? Nothing() : Just(value);}
function Just(value) { return { map: fn => Just(fn(value)), getOrElse: () => value, };}
function Nothing() { return { map: () => Nothing(), getOrElse: defaultValue => defaultValue, };}
// 相较于失败拿到 Nothing ,使用 getOrElse 返回数值或提供默认值const a = Maybe(2).map(x => x + 3).getOrElse(0);const b = Maybe(null).map(x => x + 3).getOrElse(0);
console.log(a, b) // 5, 0
以上 Maybe
Functor 的例子透过替数值包装上下文避免了需要重复撰写无值的条件判断:
// 对数值不断检查function getUserEmail(userId) { const user = findUser(userId); if (user !== null && user !== undefined) { const profile = user.profile; if (profile !== null && profile !== undefined) { const email = profile.email; if (email !== null && email !== undefined) { return email.toLowerCase(); } } }}
// 数值包装于Functor上下文中function getUserEmail(userId) { return Maybe(findUser(userId)) .map(user => user.profile) .map(profile => profile.email) .map(email => email.toLowerCase())}
Functor 定义
-
Identity
// 不改变结构的情况下改变其内容// 举例:[1, 2, 3].map(x => x) 等同 [1, 2, 3]Functor.map(x => x) === Functor -
Composition
// 连续map两个函数,应该等同于map它们的合成函数// 举例:[1, 2, 3].map(x => f(g(x))) 等同 [1, 2, 3].map(g).map(f)const f = (x) => x * 2const g = (x) => x + 1Functor.map(x => f(g(x))) === Functor.map(g).map(f)
总结
「盒子」的概念在程序语言中正式来说是:「类型构造子」,用于接收一个类型作为参数,生成一个新类型
type Maybe<T> = Just<T> | Nothing
- Functor 是一种具有
map
方法的结构,允许对其中的值进行转换同时保持结构不变。 - Functor 让函数可以基于容器进行组合,而不必关心值实际位于哪个上下文中,意味着可以被用于封装副作用或情境。
延伸阅读
- An Introduction to Functors in JavaScript: The Basics in 9 Minutes with Examples - Ijemma Onwuzulike
- Functors, Applicatives, And Monads In Pictures - adit.io
- Professor Frisby’s Mostly Adequate Guide to Functional Programming