簡體   English   中英

撰寫Monads v。Applicative Functors

[英]Composing Monads v. Applicative Functors

TypeclassopediaMonad變形金剛部分解釋說:

不幸的是,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我們可以像我們需要的那樣將它傳遞給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)

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM