撰写Monads诉Applicative Functors [英] Composing Monads v. Applicative Functors

查看:86
本文介绍了撰写Monads诉Applicative Functors的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Typeclassopedia 的Monad Transformers 部分解释了:


不幸的是,单子不会像应用函子那样编写得很好(如果您不需要Monad提供的全部功能,还可以使用Applicative的另一个原因)

观察>> = 和<$ c的类型

pre $(code>(< / code>)( *)::适用的f => f(a - > b) - > f a - > f b
(>> =):: Monad m => m a - > (a - > m b) - > mb

请解释monads不能像应用函子那样编写好。



我阅读了此答案,但您能否给予一个例子来帮助我理解?

解决方案

有几种类型的概念 * - > ; * 可能会撰写。

  newtype Compose fgx = Compose {getCompose :: f(gx) } 

在这里你可以看到 Compose kind (* - > *) - > (* - > *) - > (* - > *)很像任何好的函子组合。

所以问题是:有守法的实例吗?如下所示?

  instance(Applicative f,Applicative g)=>应用(构成f g)
实例(Monad f,Monad g)=> Monad(Compose fg)

关于monad为什么不适用于应用程序的简短答案是,虽然第一次可以写第二次不能。让我们来试试看!






我们可以用 Functor p>

  instance(Functor f,Functor g)=> Functor(Compose fg)其中
fmap f(Compose fgx)= Compose(fmap(fmap f)fgx)

这里我们看到,因为我们可以 fmap 一个 fmap -ed f 我们可以像我们需要的那样通过层 f g 来传递它。使用 pure

 实例来玩类似的游戏(Applicative f,Applicative g)=> Applicative(Compose fg)其中
pure a = Compose(纯(纯a))

(< *>)看起来很棘手,如果仔细观察,这与 fmap

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

在所有情况下,我们都可以通过 f g 层来推动我们需要的操作符。正如我们所希望的那样。



但现在我们来看看 Monad 。与其试图通过(>> =)来定义 Monad ,我将通过加入。要实现 Monad ,我们需要实现

  join :: Compose fg(撰写fgx) - >撰写fgx 

使用

  join_f :: f(fx) - > f x  - 和
join_g :: g(g x) - > gx

或者,如果剥离 newtype 噪声,我们需要

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

此时可能会清楚问题是什么 - 我们只知道如何加入 层的 f s或 g s,但在这里我们看到它们交织。你会发现我们需要一个交换性属性

  class Commute fg其中
通勤:: g(fx) - > f(gx)

现在我们可以执行

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

newtype 不可知的) join 定义为

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






所有这一切的结果是什么? Applicative s 总是 撰写,但 Monad 通勤



时, 我们什么时候可以通勤图层?以下是一些例子:

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $实例通勤(( - >)x)(( - >)y)其中
通勤=翻转

实例通勤((,)x)((,)y)
commute(y,(x,a))=(x,(y,a) )

实例通勤(( - >)x)((,)y)其中
通勤(y,xa)= \ x - > (y,xa x)

- 实例通勤((,)x)(( - >)y)不存在;试着写下自己!
-
- 或者:
- 事实证明,您需要以某种方式及时回程,使其成为
- 工作...
-
- 实例通勤((,)x)(( - >)y)其中
- 通勤yxa =(...,\ y - > let(x, a)= yxa y in a)


The Typeclassopedia's Monad Transformers section explains:

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

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) }

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)

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!


We can warm up with Functor

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

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))

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)

In all cases, we can push the operators we need "through" the layers of f and g exactly as we might hope.

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

using

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

or, if we strip off the newtype noise, we need

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

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)

and now we can implement

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

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


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 )

这篇关于撰写Monads诉Applicative Functors的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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