[英]Composing Monads v. Applicative Functors
Typeclassopedia的Monad變形金剛部分解釋說:
不幸的是,monad並不像applicative functor那樣組合(如果你不需要Monad提供的全部功能,那么使用Applicative的另一個原因)
看看>>=
和<*>
的類型,上面的陳述對我來說並不清楚。
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
請解釋一下“monad不像compative functors那樣好。”
我看了這個答案 ,但你能舉個例子來幫我理解嗎?
有幾種概念,類型* -> *
可能會“組成”。 更重要的是你可以“按順序”組合它們。
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)
關於為什么monad不能和應用程序組合的簡短答案是,雖然第一個實例可以寫入,但第二個實例不能。 我們試試吧!
我們可以和Functor
熱身
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose fgx) = Compose (fmap (fmap f) fgx)
在這里我們看到,因為我們可以fmap
fmap
-ed f
我們可以像我們需要的那樣將它傳遞給f
和g
層。 類似的游戲是pure
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure a = Compose (pure (pure a))
雖然(<*>)
看起來很棘手,但如果你仔細觀察它就是我們用fmap
和pure
完全相同的技巧。
Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx)
在所有情況下,我們都可以按照我們希望的方式“通過” f
和g
層來推動我們需要的運算符。
但現在讓我們來看看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)
with( newtype
agnostic) join
定義為
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
那么這一切的結果是什么? Applicative
s 總是 Compose
,但是Monad
s只在他們的圖層Commute
時才Compose
。
什么時候我們可以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 )
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.