前言
我不是數學家也對於範疇論 沒有太大興趣,但它深刻的與 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