2015-12-21 38 views
6

在Haskell中,monads根據函數返回和綁定來定義,其中返回的類型爲a -> m a,綁定的類型爲m a -> (a -> m b) -> m b。之前已經指出monads can also be defined in terms of return and join,其中連接是m (m a) -> m a類型的函數。綁定可以根據連接來定義,但是可能相反嗎?可以根據綁定來定義連接嗎?隨着單子,可以加入定義的綁定?

沒有加入,我不知道我會做什麼,如果我曾經以某種方式得到了一個「兩次包裹」monadic值m (m a) - 沒有一個函子或monad操作「刪除任何圖層」,可以這麼說。如果這是不可能的,爲什麼Haskell和其他monad實現在綁定方面定義它們?它似乎嚴格地比基於連接的定義更有用。

+5

注「也」:如果加入不能在綁定來定義,這將_have_是Monad'的'成員(或不會爲所有monad定義),而不是。 –

回答

9

這是可能的:

join :: Monad m => m (m a) -> m a 
join m = (m >>= id) 

>>=棘手的實例:

(>>=) :: m b -> (b -> m c) -> m c 
-- choosing b ~ m a , c ~ a 
(>>=) :: m (m a) -> (m a -> m a) -> m a 

,所以我們可以正確地選擇id第二論據。

6

是的,它是相當簡單:

join m = m >>= id 
3

綁定(>>=)做實際上是「刪除層」:

(>>=) :: Monad m => m a -> (a -> m b) -> m b 

直觀它「得到一些a出去了的m a的」,並以a -> m b函數然後饋送,然後產生一個單從結果中獲得m b

人們通常會說它需要函數參數才能在m中重新包裝其輸出,但事實並非如此。它要求功能的輸出爲東西包裝在m中,但是包裝來自何處並不重要。

在實施join的情況下,我們從「雙重包裝」開始:m (m a)。我們可以把它插入到該簽名綁定,並立即找出結合「雙包」的價值時,我們可以使用的功能類型:

m (m a) -> (m a -> m b) -> m b 

現在在這裏結合將要接收的值所使用的函數那是已經包裹在m。所以我們不必「重新包裝」任何東西;如果我們未經修改就返回它,它已經是輸出的正確類型。有效地,這是「刪除了一層包裝」 - 這適用於任何層,但最後一層。

這樣告訴我們,我們只是要與id綁定:

join = (>>= id)