Promise 是很好解決 js 異步的方案。bash
Monad 是一個 FP 中的專有名詞。 A monad is just a monoid in the category of endofunctors. Monad 就是自函子範疇上的幺半羣。app
在範疇論中,函子是範疇間的一類映射。函子也能夠解釋爲小范疇內的態射。異步
態射是範疇內對象之間的映射關係。函子與它相似,函子是範疇與範疇間的映射關係,也就是能夠經過一個函子,把一個範疇映射到另外一個範疇。函數
能夠將 Functor 理解成一個容器! 咱們用 js 中的一些東西來解釋一下可能更清楚。ui
const addThree = (x) => x + 3
const array = [ 2, 4, 6 ]
const mappedArray = array.map(addThree)
console.log(mappedArray)
// => [ 5, 7, 9 ]
複製代碼
能夠理解 Array 就是一個 Functor, 而 array 就是 Array 這個 Functor 的實例。
咱們能夠把 array 當作一個集合或者一個範疇。google
固然 Array 得是一個 Functor 的話,它還得知足 Functor 的其餘特性,這裏就不說了。spa
簡單來講,函子就是一個能夠將 function 應用到函子 value 的一個容器。3d
把一個範疇映射到自身的函子叫作自函子。code
假設咱們如今把咱們的範疇定義的更大一些,array 的元素是這個有理數集合,那麼 Array 就是一個自函子。cdn
現實中的自函子有哪些呢,掛鐘就是一個自函子。
google到數學裏定義的羣(group): G爲非空集合,若是在G上定義的二元運算 *,知足
封閉性(Closure):對於任意a,b∈G,有a*b∈G
結合律(Associativity):對於任意a,b,c∈G,有(a*b)*c=a*(b*c)
幺元 (Identity):存在幺元e,使得對於任意a∈G,e*a=a*e=a
逆元:對於任意a∈G,存在逆元a^-1,使得a^-1*a=a*a^-1=e
複製代碼
若是僅知足封閉性和結合律,則稱G是一個半羣(Semigroup);若是僅知足封閉性、結合律而且有幺元,則稱G是一個含幺半羣(Monoid)。
好比天然數這個非空集合G,加上 + 這個二元運算,就是一個幺半羣。(知足封閉性、結合律,0 就是幺元)
單子(Monad)是這樣一個自函子範疇:
其自函子對象是: M: C → C
有如下兩種天然變換:
顯然,在這個自函子範疇上構成了一個幺半羣,這個幺半羣的集合是全部自函子,其二元運算是由join決定結果的自函子複合。
在Haskell中的monad是這樣的:
class Monad m where
fmap :: (a -> b ) -> f a -> f b
return :: a -> m a
(>>= ) :: m a -> (a -> m b) -> m b
複製代碼
OK, 那 Monad js 中的 Promise 有什麼關係呢?
haskell 中 Monad 是用來隔離反作用的,Promise 在 js 中也是用來隔離反作用的,因此咱們本能能夠將兩者聯繫起來。
把 Promise 理解成一個容器。
Promise.resolve(5)
或者 new Promise((resolve, reject) => resolve(6)
是否是就是 Monad 裏面的 return 方法Promise.then
能夠近似理解成 Monad 裏面的 >>=
方法。(pure 10 :: Maybe Int) >>= \x -> return (x * x)
複製代碼
Promise.resolve(10).then(x -> x * x)
複製代碼
Promise 和 Monad 不只形似,並且神似。
那麼再回過頭講講 Monad。
haskell GHC 7.8 從新定義了以前的 Functor、Applicative 與 Monad 的關係。
Functor ⟹ Applicative ⟹ Monad
那麼看看 Applicative 是什麼。
Functor 的定義
class Functor f where
fmap :: (a -> b) -> f a -> f b
複製代碼
class Functor f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
複製代碼
ps: <*>
就是 fmap
Functor 函子 fmap :: (a -> b) -> f a -> f b
Applicative 應用函子 (<*>) :: f (a -> b) -> f a -> f b
Applicative 必須是一個 Functor,而 Functor 只能將一個 value a 裝進容器變成一個,而後與函數 a -> b 運算; 可是 Applicative 能夠將函數 a -> b 裝進容器,與原有的容器 f a 運算。
[ x + y | x <- [1..3], y <- [1..x]]
-- [2, 3, 4, 4, 5 ,6]
[1..3] >>= \x -> [1..x] >> \y -> return (x + y)
-- [2, 3, 4, 4, 5 ,6]
複製代碼
(+) <$> [1..3] <*> [1..x]
-- Not in scope: 'x'
複製代碼
y 的取值是依賴於 x 的,使用 Monad 是能夠的,可是使用 Applicative 是沒法作到的!
也就是說 Monad 後面的計算能夠依賴於前面計算的結果,可是 Applicative 中的每一個參數的計算是獨立的,後面的結果不能依賴於前面的。 通俗一些說 Monad 能夠表達 上下文(context)
的計算,Applicative 是不能夠的。
Monad在計算的時候,後一個計算問題能夠用到前面的參數,也就是說各個計算之間不是互相獨立的,而是有依賴關係的。
一樣的 Promise 是沒有上下文的。
Promise.resolve([1, 2, 3])
.then(x => [1..x])
.then(y => (x + y))
複製代碼
ps: 上述 js 的例子部分是僞代碼
我的以爲,從 Monad 的嚴格定義上, 不少約束條件 Promise 的實現都是沒有知足或者沒有嚴格知足,可是其形式及其類似。能夠說 Promise is a monad;
然而,從 context 這個核心上來看,Promise 現有的實現不能知足。能夠說 Promise is not a monad。
可是能夠確定的是 Promise 在隔離反作用上和 Monad 有殊途同歸之妙。不知在確立 Promise 規範的時候,有沒有借鑑 Monad!