组合单子与应用函子 [英] Composing Monads v. Applicative Functors
问题描述
Typeclassopedia 的 Monad Transformers 部分解释说:
不幸的是,monads 的组合不如 applicative functors 好(如果你不需要 Monad 提供的全部功能,这是使用 Applicative 的另一个原因)
Unfortunately, monads do not compose as nicely as applicative functors (yet another reason to use Applicative if you don’t need the full power that Monad provides)
看>>=
和<*>
的类型,上面的说法我不是很清楚.
Looking at the types of >>=
and <*>
, the above statement is not clear to me.
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
请解释monad 的组合不如应用函子好."
Please explain the "monads do not compose as nicely as applicative functors."
我读了这个答案,但是你能举个例子来帮助我理解吗?
I read this answer, but could you please give an example to help me understand?
推荐答案
* ->*
可能会撰写".更重要的是你可以按顺序"组合它们.
There are several notions by which types of kind * -> *
might "compose". The more important one is you can compose them "sequentially".
newtype Compose f g x = Compose { getCompose :: f (g x) }
在这里你可以看到 Compose
有种类 (* -> *) ->(* -> *) ->(* -> *)
就像任何好的函子组合都应该有的一样.
Here you can see that Compose
has kind (* -> *) -> (* -> *) -> (* -> *)
much like any good composition of functors ought to.
那么问题来了:有没有像下面这样的守法情况?
So the question is: are there law-abiding instances like the following?
instance (Applicative f, Applicative g) => Applicative (Compose f g)
instance (Monad f, Monad g) => Monad (Compose f g)
关于为什么 monad 不能像应用程序那样组合的简短回答是,虽然第一个实例可以编写,但第二个实例不能.试试吧!
And the short answer as to why monads don't compose as well as applicatives is that while the first instance can be written the second cannot. Let's try!
我们可以用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
Here we see that because we can fmap
an fmap
-ed f
we can pass it through the layers f
and g
like we need to. A similar game is played with pure
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure a = Compose (pure (pure a))
虽然 (<*>)
看起来很棘手,但如果仔细观察,这与我们在 fmap
和 pure中使用的技巧完全相同代码>.
and while (<*>)
appears tricky, if you look carefully it's the exact same trick we used with both fmap
and pure
.
Compose fgf <*> Compose fgx = Compose ((<*>) <$> fgf <*> fgx)
在所有情况下,我们都可以完全按照我们的希望推动我们需要的运算符通过"f
和 g
层.
In all cases, we can push the operators we need "through" the layers of f
and g
exactly as we might hope.
但现在让我们看看Monad
.与其尝试通过 (>>=)
定义 Monad
,我将通过 join
工作.要实现Monad
,我们需要实现
But now let's take a look at Monad
. Instead of trying to define Monad
via (>>=)
, I'm going to instead work via join
. To implement Monad
we need to implement
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
噪声,我们需要
or, if we strip off the newtype
noise, we need
join :: f (g (f (g x))) -> f (g x)
此时问题可能已经很清楚了---我们只知道如何加入连续层的f
s或g
s,但在这里我们看到它们交织.你会发现我们需要一个可交换性属性
At this point it might be clear what the problem is---we only know how to join consecutive layers of f
s or g
s, but here we see them interwoven. What you'll find is that we need a commutativity property
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 (the newtype
agnostic) join
定义为
with (the newtype
agnostic) join
defined as
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 always Compose
,但 Monad
s Compose
仅当它们的层 通勤
.
So what's the upshot of all this? Applicative
s always Compose
, but Monad
s Compose
only when their layers Commute
.
我们什么时候可以通勤
层?下面是一些例子
When can we commute
layers? Here are some examples
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 )
这篇关于组合单子与应用函子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!