Monad和Haskell中的应用的区别
我刚刚从typeclassopedia读了Monad
和Applicative
之间的区别。 我可以理解,没有join
Applicative
。 但是下面的描述对我来说看起来含糊不清,而我无法弄清楚单子计算/行为的“结果”到底是什么意思。 所以,如果我把一个值写入Maybe
,这会产生一个monad,这个“计算”的结果是什么?
让我们仔细看看(>> =)的types。 基本的直觉是它将两个计算结合到一个更大的计算中。 第一个参数ma是第一个计算。 然而,如果第二个论点只是一个mb,那将是无聊的; 那么计算就不可能互相交互(实际上,这正是Applicative的情况)。 因此,(>> =)的第二个参数具有typesa – > mb:这种types的函数在给定第一个计算结果的情况下可以产生第二个要运行的计算。 …直观地说,正是这种能力使用以前的计算输出来决定接下来运行哪些计算,使得Monad比Applicative更强大。 应用计算的结构是固定的,而Monad计算的结构可以基于中间结果而改变。
是否有一个具体的例子说明“能够使用先前计算的输出来决定接下来要运行什么计算”,Applicative没有?
我最喜欢的例子是“纯适应性的”。 我们将从分析基本的Monad实例开始
instance Monad (Either e) where return = Right Left e >>= _ = Left e Right a >>= f = fa
这个例子embedded了一个非常自然的短路概念:我们从左到右进行,一旦一个计算“失败”到Left
那么所有其他的都是一样的。 Monad
也有自然的Applicative
实例
instance Applicative (Either e) where pure = return (<*>) = ap
在return
之前, ap
只不过是从左到右的sorting:
ap :: Monad m => m (a -> b) -> ma -> mb ap mf ma = do f <- mf a <- ma return (fa)
现在,当你想要收集在计算中任何地方发生的错误消息,并以某种方式产生错误摘要时,这个Either
实例的麻烦都会变得显而易见。 这是在短路飞行。 它也面临着(>>=)
(>>=) :: ma -> (a -> mb) -> mb
如果我们把ma
看成“过去”,把mb
看作“未来”,那么只要能够运行“stepper” (a -> mb)
(>>=)
从过去产生未来。 这个“步进者”要求在未来真正存在的价值…这是不可能的。 因此(>>=)
要求短路。
所以相反,我们将实现一个不能有相应Monad
的Applicative
实例。
instance Monoid e => Applicative (Either e) where pure = Right
现在(<*>)
是值得仔细考虑的特殊部分。 它在前三种情况下执行了一些“短路”,但是在第四种情况下做了一些有趣的事情。
Right f <*> Right a = Right (fa) -- neutral Left e <*> Right _ = Left e -- short-circuit Right _ <*> Left e = Left e -- short-circuit Left e1 <*> Left e2 = Left (e1 <> e2) -- combine!
再次注意到,如果我们把左边的论点看作“过去”,将正确的论点看作“未来”,那么(<*>)
与(>>=)
相比是特殊的,因为它允许“开放”未来,过去并行,而不一定需要“过去”的结果来计算“未来”。
这意味着,我们可以直接使用我们纯粹的Applicative
来收集错误,如果链中存在任何Left
,则忽略Right
> Right (+1) <*> Left [1] <*> Left [2] > Left [1,2]
所以让我们把这个直觉放在头上。 我们不能用纯粹的应用来做什么? 那么,由于它的运作取决于在经营过去之前考察未来,我们必须能够确定未来的结构而不依赖于过去的价值。 换句话说,我们不能写
ifA :: Applicative f => f Bool -> fa -> fa -> fa
其满足以下等式
ifA (pure True) te == t ifA (pure False) te == e
而我们可以写ifM
ifM :: Monad m => m Bool -> ma -> ma -> ma ifM mbool th el = do bool <- mbool if bool then th else el
这样
ifM (return True) te == t ifM (return False) te == e
这种不可能性的产生是因为如果A根据参数计算中embedded的值恰好体现了结果计算的思想。
Just 1
描述了一个“计算”,其“结果”是1. Nothing
描述没有产生结果的计算。
Monad和Applicative的区别在于Monad有一个select。 Monad的关键区别在于能够在不同的计算path之间进行select(不仅仅是早早的爆发)。 根据前一步计算所产生的值,计算结构的其余部分可以改变。
这是这意味着什么。 在monadic链中
return 42 >>= (\x -> if x == 1 then return (x+1) else return (x-1) >>= (\y -> return (1/y) ))
if
select什么计算构造。
在适用的情况下,在
pure (1/) <*> ( pure (+(-1)) <*> pure 1 )
所有的function在“内部”计算中都起作用,没有机会拆分链条。 每个函数只是转换一个值。 计算结构的“形状”从function的angular度来看完全是“在外面”。
函数可以返回一个特殊的值来表示失败,但是它不会导致计算中的下一步被跳过。 他们都必须以特殊的方式来处理特殊的价值。 计算的形状不能根据收到的值改变。
随着单子,function本身构build计算自己的select。
这是我对@J的看法。 亚伯拉罕森的例子,为什么ifA
不能使用内部的值,例如(pure True)
。 从本质上来说,它仍然归结为Monad
在Applicative
没有的join
函数,它统一了typeclassopedia给出的两个不同的观点来解释Monad
和Applicative
之间的区别。
所以使用@J。 亚伯拉罕森的纯粹适用的例子:
instance Monoid e => Applicative (Either e) where pure = Right Right f <*> Right a = Right (fa) -- neutral Left e <*> Right _ = Left e -- short-circuit Right _ <*> Left e = Left e -- short-circuit Left e1 <*> Left e2 = Left (e1 <> e2) -- combine!
(与Monad
有类似的短路效应)和ifA
function
ifA :: Applicative f => f Bool -> fa -> fa -> fa
如果我们试图达到上述方程式,
ifA (pure True) te == t ifA (pure False) te == e
?
那么,正如已经指出的那样,最终, (pure True)
的内容,不能用于后来的计算。 但从技术上讲,这是不对的。 我们可以使用(pure True)
的内容,因为Monad
也是fmap
的Functor
。 我们可以做的:
ifA' bte = fmap b (\x -> if x then t else e)
问题是ifA'
的返回types,它是f (fa)
。 在Applicative
,没有办法将两个嵌套的Applicative
S合并为一个。 但是这个崩溃的function正是Monad
所join
function。 所以,
ifA = join . ifA'
将满足ifA
的方程,如果我们可以适当地实现join
。 这里所缺less的Applicative
就是join
函数。 换句话说,我们可以以某种方式使用Applicative
以前结果的结果。 但是在Applicative
框架中这样做会涉及到将返回值的types扩大到嵌套的应用价值,我们无法恢复到单层应用价值。 这将是一个严重的问题,因为例如我们不能适当地使用Applicative
S来编写函数。 使用join
修复问题,而连接的引入促进了Monad
的Applicative
。
差别的关键可以在ap
types和=<<
的types中观察到。
ap :: m (a->b) -> (m a->mb) =<< :: (a->mb) -> (m a->mb)
在这两种情况下都有ma
,但只有在第二种情况下, ma
可以决定函数(a->mb)
被应用。 反过来,函数(a->mb)
可以通过生成不含“ b
(如[]
, Nothing
或Left
)的(a->mb)
来“决定”下一个函数bound是否被应用。
在Applicative
, m (a->b)
函数没有办法做出这样的“决定” – 它们总是产生一个typesb
的值。
f 1 = Nothing -- here f "decides" to produce Nothing fx = Just x Just 1 >>= f >>= g -- g doesn't get applied, because f decided so.
在Applicative
这是不可能的,所以不能显示一个例子。 最接近的是:
f 1 = 0 fx = x g <$> f <$> Just 1 -- oh well, this will produce Just 0, but can't stop g -- from getting applied
但是下面的描述对我来说看起来含糊不清,而我无法弄清楚单子计算/行为的“结果”到底是什么意思。
那么这种模糊性是有点故意的,因为一个单子计算的“结果”是依赖于每种types的东西。 最好的答案有点重复:“结果”(或结果 ,因为可以有多个)是实例实现(>>=) :: Monad m => ma -> (a -> mb) -> mb
调用函数参数。
所以,如果我把一个值写入
Maybe
,这会产生一个monad,这个“计算”的结果是什么?
Maybe
monad看起来像这样:
instance Monad Maybe where return = Just Nothing >>= _ = Nothing Just a >>= k = ka
这里唯一符合“结果”条件的是>>=
的第二个等式,因为它是唯一被“馈送”到第二个参数>>=
。
其他答案已经深入了解了ifM
和ifM
区别,所以我想我会强调另一个重要的区别: 应用程序组成,monad不 。 对于Monad
,如果你想创build一个结合两个现有效果的Monad
,你必须将其中的一个改写为monad变换器。 相反,如果您有两个Applicatives
,则可以轻松地制作更复杂的Applicatives
,如下所示。 (代码从transformers
拷贝)
-- | The composition of two functors. newtype Compose fga = Compose { getCompose :: f (ga) } -- | The composition of two functors is also a functor. instance (Functor f, Functor g) => Functor (Compose fg) where fmap f (Compose x) = Compose (fmap (fmap f) x) -- | The composition of two applicatives is also an applicative. instance (Applicative f, Applicative g) => Applicative (Compose fg) where pure x = Compose (pure (pure x)) Compose f <*> Compose x = Compose ((<*>) <$> f <*> x) -- | The product of two functors. data Product fga = Pair (fa) (ga) -- | The product of two functors is also a functor. instance (Functor f, Functor g) => Functor (Product fg) where fmap f (Pair xy) = Pair (fmap fx) (fmap fy) -- | The product of two applicatives is also an applicative. instance (Applicative f, Applicative g) => Applicative (Product fg) where pure x = Pair (pure x) (pure x) Pair fg <*> Pair xy = Pair (f <*> x) (g <*> y) -- | The sum of a functor @f@ with the 'Identity' functor data Lift fa = Pure a | Other (fa) -- | The sum of two functors is always a functor. instance (Functor f) => Functor (Lift f) where fmap f (Pure x) = Pure (fx) fmap f (Other y) = Other (fmap fy) -- | The sum of any applicative with 'Identity' is also an applicative instance (Applicative f) => Applicative (Lift f) where pure = Pure Pure f <*> Pure x = Pure (fx) Pure f <*> Other y = Other (f <$> y) Other f <*> Pure x = Other (($ x) <$> f) Other f <*> Other y = Other (f <*> y)
现在,如果我们添加Constant
函数/应用:
newtype Constant ab = Constant { getConstant :: a } instance Functor (Constant a) where fmap f (Constant x) = Constant x instance (Monoid a) => Applicative (Constant a) where pure _ = Constant mempty Constant x <*> Constant y = Constant (x `mappend` y)
…我们可以从“ Lift
与Constant
”中的其他答案中组合“应用”
type Error ea = Lift (Constant e) a