Haskell中Monad和Applicative的区别 [英] Difference between Monad and Applicative in Haskell

查看:149
本文介绍了Haskell中Monad和Applicative的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚从 typeclassopedia 阅读了关于 Monad Applicative 。我可以理解, Applicative 中没有 join 。但是下面的描述对我来说看起来含糊不清,我无法弄清楚单子计算/行为的结果究竟意味着什么。因此,如果我将一个值放入 Maybe 中,这会产生monad,那么这个计算的结果是什么?


让我们仔细看看(>> =)的类型。基本直觉是
,它将两次计算合并为一次更大的计算。
的第一个参数m a是第一个计算。然而,如果第二个参数仅仅是m b,那么它会是无聊的;那么就不会有
的计算方式与另一个进行交互(实际上,这个
恰恰是Applicative的情况)。因此,
(>> =)的第二个参数具有类型a - > m b:一个这种类型的函数,如果第一个计算结果为
,则可以产生第二个要运行的计算。
...直观地说,正是这种能力使用前面
计算的输出来决定下一步要运行哪些计算,这使得Monad
比Applicative更强大。应用程序
计算的结构是固定的,而Monad计算的结构可以根据中间结果更改

是否有一个具体的例子说明能够使用先前计算的输出来决定下一步要运行哪些计算,这是Applicative没有的?

解决方案

我最喜欢的例子是纯适用性的。我们将从分析Monad实例的基础开始,分析其中的任何一个实例Monad(E或e)其中
return = Right
Left e>> = _ = Left e
Right a>> = f = fa

这个实例嵌入了一个非常自然的短路概念:我们从左到右,一旦单个计算失败进入 Left ,那么其余的都做得很好。还有自然的 Applicative 实例,任何 Monad 都有



<$ ($ e $)
pure = return
(< *>)= ap
pre>

其中 ap 无非是在返回之前从左到右排序

  ap :: Monad m => m(a  - > b) - > m a  - > mb 
ap mf ma = do
f < - mf
a < - ma
return(fa)






现在遇到这个或者 d喜欢收集计算中发生的任何错误消息,并以某种方式产生错误摘要。这在短路的情况下飞行。它也面临着类型(>> =)

 (>> =):: ma  - > (a  - > m b) - > mb 

如果我们将 ma 看作过去和 mb 作为未来,然后(>> =)产生过去的未来只要它可以运行步进器(a - > mb)。这个步进器要求 a 的值在将来确实存在......并且这对是不可能的 。因此,(>> =) 要求短路。我们将实现一个 Applicative 实例,它不能有相应的 Monad

  instance Monoid e =>应用(或e)其中
pure = Right

现在执行(< *>)是值得仔细考虑的特殊部分。它在第一个 3 情况下执行了一些短路,但在第四个情况下做了一些有趣的事。

code>右边的f< *>右a =右(f a) - 中性
左e *右_ =左e - 短路
右_< *>左e =左e - 短路
左e1 *左e2 =左(e1≠e2) - 合并!

再次注意,如果我们将左边的论点看作过去,将正确的论点看作与(>> =)相比,未来(*)是特殊的,因为它是允许为了计算未来而开放未来和过去,而不一定需要过去的结果。

这意味着,我们可以直接使用纯粹的 Applicative 或者来收集错误,忽略 Right s,如果在链中存在 Left s

 >右(+1)* *左[1] *左[2] 
>左[1,2]






直觉头脑。我们不能用纯粹的应用程序或者来做什么?那么,由于它的运作取决于在运行过去之前检查未来,我们必须能够确定未来的结构而不依赖于过去的价值。换句话说,我们不能写

  ifA :: Applicative f => f Bool  - > f a  - > f a  - > fa 

满足以下等式:

  ifA(纯真)te == t 
ifA(纯假)te == e

我们可以写 ifM

 <$ c $ ifM :: Monad m => m Bool  - > m a  - > m a  - > ma 
ifM mbool th el = do
bool< - mbool
if bool then th else el

使得

  ifM(返回True)te == t 
ifM返回False)te == e

由于 ifA 完全体现了结果计算的思想,取决于参数计算中嵌入的值。


I just read the following from typeclassopedia about the difference between Monad and Applicative. I can understand that there is no join in Applicative. But the following description looks vague to me and I couldn't figure out what exactly is meant by "the result" of a monadic computation/action. So, if I put a value into Maybe, which makes a monad, what is the result of this "computation"?

Let’s look more closely at the type of (>>=). The basic intuition is that it combines two computations into one larger computation. The first argument, m a, is the first computation. However, it would be boring if the second argument were just an m b; then there would be no way for the computations to interact with one another (actually, this is exactly the situation with Applicative). So, the second argument to (>>=) has type a -> m b: a function of this type, given a result of the first computation, can produce a second computation to be run. ... Intuitively, it is this ability to use the output from previous computations to decide what computations to run next that makes Monad more powerful than Applicative. The structure of an Applicative computation is fixed, whereas the structure of a Monad computation can change based on intermediate results.

Is there a concrete example illustrating "ability to use the output from previous computations to decide what computations to run next", which Applicative does not have?

解决方案

My favorite example is the "purely applicative Either". We'll start by analyzing the base Monad instance for Either

instance Monad (Either e) where
  return = Right
  Left e  >>= _ = Left e
  Right a >>= f = f a

This instance embeds a very natural short-circuiting notion: we proceed from left to right and once a single computation "fails" into the Left then all the rest do as well. There's also the natural Applicative instance that any Monad has

instance Applicative (Either e) where
  pure  = return
  (<*>) = ap

where ap is nothing more than left-to-right sequencing before a return:

ap :: Monad m => m (a -> b) -> m a -> m b
ap mf ma = do 
  f <- mf
  a <- ma
  return (f a)


Now the trouble with this Either instance comes to light when you'd like to collect error messages which occur anywhere in a computation and somehow produce a summary of errors. This flies in the face of short-circuiting. It also flies in the face of the type of (>>=)

(>>=) :: m a -> (a -> m b) -> m b

If we think of m a as "the past" and m b as "the future" then (>>=) produces the future from the past so long as it can run the "stepper" (a -> m b). This "stepper" demands that the value of a really exists in the future... and this is impossible for Either. Therefore (>>=) demands short-circuiting.

So instead we'll implement an Applicative instance which cannot have a corresponding Monad.

instance Monoid e => Applicative (Either e) where
  pure = Right

Now the implementation of (<*>) is the special part worth considering carefully. It performs some amount of "short-circuiting" in its first 3 cases, but does something interesting in the fourth.

  Right f <*> Right a = Right (f a)     -- neutral
  Left  e <*> Right _ = Left e          -- short-circuit
  Right _ <*> Left  e = Left e          -- short-circuit
  Left e1 <*> Left e2 = Left (e1 <> e2) -- combine!

Notice again that if we think of the left argument as "the past" and the right argument as "the future" then (<*>) is special compared to (>>=) as it's allowed to "open up" the future and the past in parallel instead of necessarily needing results from "the past" in order to compute "the future".

This means, directly, that we can use our purely Applicative Either to collect errors, ignoring Rights if any Lefts exist in the chain

> Right (+1) <*> Left [1] <*> Left [2]
> Left [1,2]


So let's flip this intuition on its head. What can we not do with a purely applicative Either? Well, since its operation depends upon examining the future prior to running the past, we must be able to determine the structure of the future without depending upon values in the past. In other words, we cannot write

ifA :: Applicative f => f Bool -> f a -> f a -> f a

which satisfies the following equations

ifA (pure True)  t e == t
ifA (pure False) t e == e

while we can write ifM

ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM mbool th el = do
  bool <- mbool
  if bool then th else el

such that

ifM (return True)  t e == t
ifM (return False) t e == e

This impossibility arises because ifA embodies exactly the idea of the result computation depending upon the values embedded in the argument computations.

这篇关于Haskell中Monad和Applicative的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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