组合单子与应用函子 [英] Composing Monads v. Applicative Functors

查看:31
本文介绍了组合单子与应用函子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TypeclassopediaMonad 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 我们可以通过层 fg 就像我们需要的那样.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))

虽然 (<*>) 看起来很棘手,但如果仔细观察,这与我们在 fmappure.

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)

在所有情况下,我们都可以完全按照我们的希望推动我们需要的运算符通过"fg 层.

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)

此时问题可能已经很清楚了---我们只知道如何加入连续层的fs或gs,但在这里我们看到它们交织.你会发现我们需要一个可交换性属性

At this point it might be clear what the problem is---we only know how to join consecutive layers of fs or gs, 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

<小时>

那么这一切的结果是什么?Applicatives always Compose,但 Monads Compose 仅当它们的层 通勤.


So what's the upshot of all this? Applicatives always Compose, but Monads 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆