promise is a monad?

Promise 是很好解決 js 異步的方案。bash

Monad 單子

Monad 是一個 FP 中的專有名詞。 A monad is just a monoid in the category of endofunctors. Monad 就是自函子範疇上的幺半羣。app

Functor 函子

在範疇論中,函子是範疇間的一類映射。函子也能夠解釋爲小范疇內的態射。異步

態射是範疇內對象之間的映射關係。函子與它相似,函子是範疇與範疇間的映射關係,也就是能夠經過一個函子,把一個範疇映射到另外一個範疇。函數

能夠將 Functor 理解成一個容器! 咱們用 js 中的一些東西來解釋一下可能更清楚。ui

const addThree = (x) => x + 3
const array = [ 2, 4, 6 ]
const mappedArray = array.map(addThree)
console.log(mappedArray)
// => [ 5, 7, 9 ]
複製代碼

functor
能夠理解 Array 就是一個 Functor, 而 array 就是 Array 這個 Functor 的實例。

咱們能夠把 array 當作一個集合或者一個範疇。google

  • array 裏面的 二、四、6,應用了 addThree 與 mappedArray 中的 五、七、9 一一對應;

固然 Array 得是一個 Functor 的話,它還得知足 Functor 的其餘特性,這裏就不說了。spa

簡單來講,函子就是一個能夠將 function 應用到函子 value 的一個容器。3d

endoFunctor 自函子

把一個範疇映射到自身的函子叫作自函子。code

假設咱們如今把咱們的範疇定義的更大一些,array 的元素是這個有理數集合,那麼 Array 就是一個自函子。cdn

現實中的自函子有哪些呢,掛鐘就是一個自函子。

semigroup(半羣) 與 monoid(幺半羣)

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

  • 有如下兩種天然變換:

    • unit(X): I → M X 是 C 中的對象,I 是 id 自函子
    • join(X): M × M → M

顯然,在這個自函子範疇上構成了一個幺半羣,這個幺半羣的集合是全部自函子,其二元運算是由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
複製代碼

Promise 和 Monad

OK, 那 Monad js 中的 Promise 有什麼關係呢?

haskell 中 Monad 是用來隔離反作用的,Promise 在 js 中也是用來隔離反作用的,因此咱們本能能夠將兩者聯繫起來。

把 Promise 理解成一個容器。

  1. Promise.resolve(5) 或者 new Promise((resolve, reject) => resolve(6) 是否是就是 Monad 裏面的 return 方法
  2. Promise.then 能夠近似理解成 Monad 裏面的 >>= 方法。
(pure 10 :: Maybe Int) >>= \x -> return (x * x)
複製代碼
Promise.resolve(10).then(x -> x * x)
複製代碼

Promise 和 Monad 不只形似,並且神似。

那麼再回過頭講講 Monad。

Functor、Applicative 與 Monad

haskell GHC 7.8 從新定義了以前的 Functor、Applicative 與 Monad 的關係。

Functor ⟹ Applicative ⟹ Monad

那麼看看 Applicative 是什麼。

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 運算。

Applicative 與 Monad 的區別

[ 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!

相關文章
相關標籤/搜索