Haskell中Monad和Applicative的区别 [英] Difference between Monad and Applicative in Haskell
问题描述
我刚刚从 typeclassopedia 阅读了关于 Monad $之间区别的内容c $ c>和
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 情况下执行了一些短路,但在第四个情况下做了一些有趣的事。
左e *右_ =左e - 短路
右_< *>左e =左e - 短路
左e1 *左e2 =左(e1≠e2) - 合并!
再次注意,如果我们将左边的论点看作过去,将正确的论点看作与 这意味着,我们可以直接使用纯粹的 直觉头脑。我们不能用纯粹的应用程序 满足以下等式: 我们可以写 使得 由于 I just read the following from typeclassopedia about the difference between 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 This instance embeds a very natural short-circuiting notion: we proceed from left to right and once a single computation "fails" into the where
Now the trouble with this If we think of So instead we'll implement an Now the implementation of Notice again that if we think of the left argument as "the past" and the right argument as "the future" then This means, directly, that we can use our purely
So let's flip this intuition on its head. What can we not do with a purely applicative which satisfies the following equations while we can write such that This impossibility arises because 这篇关于Haskell中Monad和Applicative的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!(>> =)
相比,未来
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
完全体现了结果计算的思想,取决于参数计算中嵌入的值。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"?
instance Monad (Either e) where
return = Right
Left e >>= _ = Left e
Right a >>= f = f a
Left
then all the rest do as well. There's also the natural Applicative
instance that any Monad
hasinstance Applicative (Either e) where
pure = return
(<*>) = ap
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)
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
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.Applicative
instance which cannot have a corresponding Monad
.instance Monoid e => Applicative (Either e) where
pure = Right
(<*>)
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!
(<*>)
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".Applicative
Either
to collect errors, ignoring Right
s if any Left
s exist in the chain> Right (+1) <*> Left [1] <*> Left [2]
> Left [1,2]
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 writeifA :: Applicative f => f Bool -> f a -> f a -> f a
ifA (pure True) t e == t
ifA (pure False) t e == e
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
ifM (return True) t e == t
ifM (return False) t e == e
ifA
embodies exactly the idea of the result computation depending upon the values embedded in the argument computations.