我刚从typeclassopedia了解了Monad
和Applicative
之间的区别。我可以理解join
中没有Applicative
。但是下面的描述看起来含糊不清,我无法弄清楚"结果"的确切含义。一元计算/行动。所以,如果我将一个值放入Maybe
,这是一个monad,那么"计算"的结果是什么?
让我们更仔细地看一下(>> =)的类型。基本的直觉是 它将两个计算组合成一个更大的计算。该 第一个参数m a是第一个计算。但是,它会 如果第二个论点只是一个m b,那就很无聊;那就没有了 计算彼此交互的方式(实际上,这个 正是适用的情况)。所以,第二个论点 (>> =)具有类型a - > m b:给定结果的这种类型的函数 第一次计算,可以产生第二次运算。 ...直观地说,正是这种能力使用了之前的输出 用于决定接下来运行哪些计算的计算使Monad成为可能 比Applicative更强大。申请人的结构 计算是固定的,而Monad计算的结构可以 根据中间结果改变。
是否有一个具体的例子说明"能否使用先前计算的输出来决定接下来运行的计算"哪个应用程序没有?
答案 0 :(得分:61)
我最喜欢的例子是“纯粹适用于任何一个”。我们首先分析Either的基础Monad实例
instance Monad (Either e) where
return = Right
Left e >>= _ = Left e
Right a >>= f = f a
这个例子嵌入了一个非常自然的短路概念:我们从左到右进行,一旦单个计算“失败”进入Left
,那么所有其他人也会这样做。任何Applicative
都有
Monad
实例
instance Applicative (Either e) where
pure = return
(<*>) = ap
其中ap
只不过是return
之前从左到右的排序:
ap :: Monad m => m (a -> b) -> m a -> m b
ap mf ma = do
f <- mf
a <- ma
return (f a)
现在,当您想要收集计算中出现的错误消息并以某种方式产生错误摘要时,此Either
实例会出现问题。面对短路,这种情况很严重。它也面对(>>=)
(>>=) :: m a -> (a -> m b) -> m b
如果我们将m a
视为“过去”而将m b
视为“未来”,那么(>>=)
会产生过去的未来,只要它可以运行“步进” (a -> m b)
。这个“步进者”要求a
的价值确实存在于未来...... Either
这是不可能的。因此(>>=)
要求短路。
相反,我们将实现一个Applicative
实例,该实例不能有相应的Monad
。
instance Monoid e => Applicative (Either e) where
pure = Right
现在(<*>)
的实施是值得仔细考虑的特殊部分。它在第一个 3 案例中执行了一些“短路”,但在第四个案例中做了一些有趣的事情。
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!
再次注意,如果我们将左边的参数视为“过去”而将右边的参数视为“未来”,那么(<*>)
与(>>=)
相比是特殊的,因为它允许“打开”未来和过去并行而不是必然需要“过去”的结果来计算“未来”。
这意味着,我们可以直接使用纯Applicative
Either
来收集错误,如果链中存在任何Right
,则忽略Left
> Right (+1) <*> Left [1] <*> Left [2]
> Left [1,2]
所以让我们把这个直觉放在头上。我们不能用纯粹适用的Either
做什么?那么,由于其运作取决于在运行过去之前检查未来,我们必须能够在不依赖于过去的价值的情况下确定未来的结构。换句话说,我们不能写
ifA :: 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
完全体现了结果计算的思想,取决于参数计算中嵌入的值。
答案 1 :(得分:38)
Just 1
描述了&#34;计算&#34;,其结果&#34;是Nothing
描述了一个不产生结果的计算。
Monad和Applicative之间的区别在于Monad可以选择。 Monads的关键区别在于能够在计算中选择不同的路径(不仅仅是提前爆发)。根据前一步计算产生的值,其余的计算结构可以改变。
这就是这意味着什么。在monadic链中
return 42 >>= (\x ->
if x == 1
then
return (x+1)
else
return (x-1) >>= (\y ->
return (1/y) ))
if
选择要构建的计算。
如果是Applicative,请
pure (1/) <*> ( pure (+(-1)) <*> pure 1 )
所有功能都起作用&#34;内部&#34;计算,没有机会打破链条。每个函数只是转换它所馈送的值。 &#34;形状&#34;计算结构完全是在外面&#34;从功能&#39;观点看法。
函数可以返回一个特殊值来指示失败,但它不能导致跳过计算中的后续步骤。他们都必须以特殊的方式处理特殊价值。计算的形状不能根据接收的值改变。
使用monad,函数本身可以根据自己的选择构建计算。
答案 2 :(得分:15)
这是我对@J的看法。 Abrahamson关于为什么ifA
无法使用内部值的示例(pure True)
。从本质上讲,它仍然归结为join
中Monad
Applicative
函数的缺失,Monad
统一typeclassopedia中给出的两个不同视角来解释Applicative
之间的差异{1}}和Either
。
所以使用@J。亚伯拉罕森的纯粹适用的例子instance Monoid e => Applicative (Either e) where
pure = Right
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!
:
Either
(与Monad
ifA
具有类似的短路效果)和ifA :: Applicative f => f Bool -> f a -> f a -> f a
函数
ifA (pure True) t e == t
ifA (pure False) t e == e
如果我们试图实现上述等式,该怎么办:
(pure True)
嗯,正如已经指出的那样,最终,(pure True)
的内容不能用于以后的计算。但从技术上讲,这是不对的。我们可以使用Monad
的内容,因为Functor
也是fmap
ifA' b t e = fmap (\x -> if x then t else e) b
。我们可以这样做:
ifA'
问题在于f (f a)
的返回类型,即Applicative
。在Applicative
中,无法将两个嵌套的join
S合并为一个。但是这个折叠函数正是Monad
中ifA = join . ifA'
执行的功能。所以,
ifA
如果我们能够恰当地实现join
,将满足Applicative
的等式。这里缺少的join
正是Applicative
函数。换句话说,我们可以某种方式使用Applicative
中先前结果的结果。但是在Applicative
框架中这样做会涉及将返回值的类型扩展为嵌套的应用值,我们无法将其返回到单级应用值。这将是一个严重的问题,因为,例如,我们不能恰当地使用join
S来组合函数。使用join
修复了该问题,但Applicative
的介绍将Monad
提升为{{1}}。
答案 3 :(得分:12)
ap
类型与=<<
的类型可以观察到差异的关键。
ap :: m (a->b) -> (m a->m b)
=<< :: (a->m b) -> (m a->m b)
在这两种情况下都有m a
,但仅在第二种情况m a
可以决定是否应用函数(a->m b)
。反过来,函数(a->m b)
可以“决定”接下来绑定的函数是否被应用 - 通过生成不包含m b
的{{1}}(如b
, []
或Nothing
)。
在Left
中,Applicative
内部的函数无法做出此类“决定” - 它们始终生成m (a->b)
类型的值。
b
在f 1 = Nothing -- here f "decides" to produce Nothing
f x = Just x
Just 1 >>= f >>= g -- g doesn't get applied, because f decided so.
中这是不可能的,所以无法显示示例。最接近的是:
Applicative
答案 4 :(得分:2)
但是下面的描述看起来含糊不清,我无法弄清楚monadic计算/动作的“结果”究竟是什么意思。
嗯,这种模糊性有些刻意,因为“结果”是一元计算的东西取决于每种类型。最好的答案有点重复:“结果”(或结果,因为可能有多个)是实例的(>>=) :: Monad m => m a -> (a -> m b) -> m b
实现调用函数参数的任何值用。
那么,如果我将一个值放入
Maybe
,这是一个monad,这个“计算”的结果是什么?
Maybe
monad看起来像这样:
instance Monad Maybe where
return = Just
Nothing >>= _ = Nothing
Just a >>= k = k a
这里唯一有资格作为“结果”的是a
的第二个等式中的>>=
,因为它是唯一被“喂”到{的第二个参数的东西{1}}。
其他答案已深入探讨了>>=
与ifA
之间的差异,所以我认为我会强调另一个显着差异:应用程序撰写,monad不 。对于ifM
s,如果要创建一个组合了两个现有效果的Monad
,则必须将其中一个重写为monad变换器。相反,如果你有两个Monad
,你可以轻松地制作一个更复杂的一个,如下所示。 (代码是从transformers
复制的。)
Applicatives
现在,如果我们添加-- | The composition of two functors.
newtype Compose f g a = Compose { getCompose :: f (g a) }
-- | The composition of two functors is also a functor.
instance (Functor f, Functor g) => Functor (Compose f g) 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 f g) where
pure x = Compose (pure (pure x))
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
-- | The product of two functors.
data Product f g a = Pair (f a) (g a)
-- | The product of two functors is also a functor.
instance (Functor f, Functor g) => Functor (Product f g) where
fmap f (Pair x y) = Pair (fmap f x) (fmap f y)
-- | The product of two applicatives is also an applicative.
instance (Applicative f, Applicative g) => Applicative (Product f g) where
pure x = Pair (pure x) (pure x)
Pair f g <*> Pair x y = Pair (f <*> x) (g <*> y)
-- | The sum of a functor @f@ with the 'Identity' functor
data Lift f a = Pure a | Other (f a)
-- | The sum of two functors is always a functor.
instance (Functor f) => Functor (Lift f) where
fmap f (Pure x) = Pure (f x)
fmap f (Other y) = Other (fmap f y)
-- | 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 (f x)
Pure f <*> Other y = Other (f <$> y)
Other f <*> Pure x = Other (($ x) <$> f)
Other f <*> Other y = Other (f <*> y)
仿函数/ applicative:
Constant
...我们可以从newtype Constant a b = 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)
和Either
中的其他回复中汇总“适用Lift
”:
Constant
答案 5 :(得分:-1)
我想分享我对“难解之谜”的看法,因为据我了解,上下文中的所有内容都得到了应用,例如:
iffy :: Applicative f => f Bool -> f a -> f a -> f a
iffy fb ft fe = cond <$> fb <*> ft <*> fe where
cond b t e = if b then t else e
case 1>> iffy (Just True) (Just “True”) Nothing ->> Nothing
upps应该只是“ True” ...但是
case 2>> iffy (Just False) (Just “True”) (Just "False") ->> Just "False"
(在上下文中选择“好”) 我以这种方式向自己解释了这一点,就在计算结束之前,如果>> 1我们在“链”中得到了类似的内容:
Just (Cond True "True") <*> something [something being "accidentaly" Nothing]
根据“应用”的定义,其评估为:
fmap (Cond True "True") something
当“某物” 为时,根据Functor约束,什么都不会变成什么(fmap over Nothing给出了什么)。而且不可能用故事的结尾定义“ fmap f Nothing = something”的函子。