上一篇 Functor 1 ,提過這種盒子 context (shape) 裡裝著值 Value ,並滿足 can be map over 特性也是 Functor, 這一篇就會來好好探究這種 Data Type 的 Functor。這種 Data Type 的好處就是不止是單純的值,還包括相關的屬性跟方法。
Note. 接下來一系列的圖都是參考經典圖 Aditya Bhargava 然後為了加深印象再自己用 Canva 畫的。
先從我們最熟悉的基本值 Value
開始說
我們都知道如何處理值跟值之間運算,可以寫一個 function
輸入一個值 (input) 然後得到另一個值 (output)
const add = x => x + 3;
add(2); // 5
但若包成 Box
原來的 function
就不能用了,因為 + 3 這個涵式是給基本值用的
我們需要重新定義一種神奇的運算 map
能接受 Functor 當作輸入參數
const Box = x => ({
map: f => Box(f(x)), // 神奇的運算在此
fold: f => f(x), // 取值 remove from the box
});
const result = Box(2)
.map(x => x + 3) // Box(5)
map
內部,實際上是這樣運作的: 取出值,用 function 處理過後再裝回 Context 裡
const Box = x => ({
map: f => Box(f(x)),
fold: f => f(x), // 取值 remove from the box
})
const result = Box(2)
.fold(x => x + 3) // 5
JS 本質仍不是一個 FP 語言,所以應用到了 Functor 的概念我只能盡量解釋,主要目的就是他可以讓 Value 維持在 Box 裡面,但仍然可以擁有一些 State 跟 method (像 OOP 的 class)。這樣讓 Box 自己運用函式也可以達成抽象化,我們並不是直接呼叫函式,而是說
Hey Box, call that function to me
這不就是 FP 概念嗎
那你可能會覺得那幹嘛不用 Class 就好,再說一次並沒有哪種寫法是絕對完美,只是了解以後多一種選擇而已。同一個問題你可以用數十種方式方式達成目的,但用 Box 好處是你不用去管理 State 的狀態,例如
/**
@parm {value} x
*/
const Box = x => ({
map: f => Box(f(x)),
value: x // 只是容易看值用而已
})
Box('a')
.map(x => x.toUpperCase())
.value; //'A'
x => x.toUpperCase()
, x
在這邊用完之後就會完全消失,你不用煩惱現在 x 到底是多少,但 Class 的狀態會是一直存在的,需要去記憶他現在到底是多少。
另一個好處 input 的型別就算不同,但方法也可以共享,例如 input 是 String
但想用 Array
的 map
// input 為 Array 寫法
['a']
.map(x => x.toUpperCase())
// ['A']
/**
@parm {value} x
*/
const Box = x => ({
map: f => Box(f(x)),
value: x // 只是容易看值用而已
})
Box('a')
.map(x => x.toUpperCase())
.value; //'A'
map
看起來像 recursive,但其實不是,他只是把運算過的值放回 Box 而已。
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。