2015-04-05 44 views
26

Typeclassopedia單子變形金剛部分解釋:撰寫單子v應用型函子

不幸的是,單子不作爲構成很好的應用性函子(另一個理由使用應用型,如果你不需要這單子提供全功率)

縱觀各類>>=<*>,上面的語句,我不清楚。

(<*>) :: Applicative f => f (a -> b) -> f a -> f b 
(>>=) :: Monad m => m a -> (a -> m b) -> m b 

請解釋「單子不像應用函子那麼好」。

我看了這個answer,但請您舉個例子來幫助我理解?

+5

由於這是「單子變壓器」部分下,相信所有這些意味着,'(應用型女,應用型克)=>應用型(C FG)',但它不是的情況下(Monad f,Monad g)=> Monad(C fg)',其中'數據C fga = C(f(ga))'。如果您想深入瞭解爲什麼會出現這種情況,那麼嘗試編寫後一個實例可能會說明問題。 – user2407038 2015-04-05 04:07:38

+1

另請參見[具有示例顯示monads在組合下(有證明)沒有關閉?](http://stackoverflow.com/q/13034229/1333025) – 2015-04-05 05:13:32

回答

36

種類* -> *可能「構成」的幾種概念。更重要的是你可以「順序」組成它們。

newtype Compose f g x = Compose { getCompose :: f (g x) } 

在這裏你可以看到Compose有一種(* -> *) -> (* -> *) -> (* -> *)很像仿函數中的任何好的構圖應該。

所以問題是:是否有類似以下的守法實例?

instance (Applicative f, Applicative g) => Applicative (Compose f g) 
instance (Monad f,  Monad g)  => Monad  (Compose f g) 

而簡短的回答,爲什麼單子不構成以及applicatives是,雖然第一個實例可以被寫入第二不能。咱們試試吧!


我們可以熱身與Functor

instance (Functor f,  Functor g)  => Functor  (Compose f g) where 
    fmap f (Compose fgx) = Compose (fmap (fmap f) fgx) 

在這裏,我們看到,因爲我們可以fmapfmap -ed f,我們可以通過它經過層層fg像我們需要。類似的遊戲的玩法是pure

instance (Applicative f, Applicative g) => Applicative (Compose f g) where 
    pure a = Compose (pure (pure a)) 

同時(<*>)出現棘手的,如果你仔細看,這是我們既fmappure使用完全相同的伎倆。

Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx) 

在所有情況下,我們可以「穿越」的fg層層推進,我們需要的正是運營商,我們可能希望。

但現在讓我們來看看Monad。我不打算通過(>>=)來定義Monad,而是通過join工作。要實現Monad我們需要實現

join :: Compose f g (Compose f g x) -> Compose f g x 

使用

join_f :: f (f x) -> f x -- and 
join_g :: g (g x) -> g x 

,或者,如果我們剝去newtype噪音,我們需要

join :: f (g (f (g x))) -> f (g x) 

在這一點上它可能是清楚的問題是---我們只知道如何連接連續f s或g s,但在這裏我們看到他們交織。你會發現,我們需要一個可交換財產

class Commute f g where 
    commute :: g (f x) -> f (g x) 

,現在我們可以實現

instance (Monad f, Monad g, Commute f g) => Monad (Compose f g) 

與(在newtype不可知)定義爲

join :: f (g (f (g x))) -> f (g x) 
join fgfgx = fgx where 
    ffggx :: f (f (g (g x))) 
    ffggx = fmap commute fgfgx 
    fggx :: f (g (g x)) 
    fggx = join_f ffggx 
    fgx :: f (g x) 
    fgx = fmap join_g fggx 

join

所有這一切的結果是什麼? Applicative s 總是Compose,但是Monad s Compose只有當他們的層Commute

我們什麼時候可以commute層?下面是一些例子

instance Commute ((->) x) ((->) y) where 
    commute = flip 

instance Commute ((,) x) ((,) y) where 
    commute (y, (x, a)) = (x, (y, a)) 

instance Commute ((->) x) ((,) y) where 
    commute (y, xa) = \x -> (y, xa x) 

-- instance Commute ((,) x) ((->) y) does not exist; try to write yourself! 
-- 
-- OR: 
-- It turns out that you need to somehow "travel back in time" to make it 
-- work... 
-- 
-- instance Commute ((,) x) ((->) y) where 
-- commute yxa = (..., \y -> let (x, a) = yxa y in a) 
+5

可能值得一提的是,由於'Traversable'類類提供這個類型的簽名是'sequence :: Monad m => t(ma) - > m(ta)',你可以將'NestedMonads'定義爲'type NestedMonads mt =(Monad m,Traversable t ,然後'bibind :: NestedMonads mt => m(ta) - >(a - > m(tb)) - > m(tb)'。 [code](http://lpaste.net/130180)。 – user3237465 2015-04-05 08:23:42

+0

這給出了一個很好的直覺,以便Commute能做什麼,謝謝! – 2015-04-05 14:57:26

+1

在這裏添加'Traversable'似乎不是內部monad上的正確約束可能很有用。看到答案[這裏](http://stackoverflow.com/a/42298943/2684007)。 – 2017-02-17 18:05:17