tin*_*lyx 50 monads haskell applicative
我刚才读从以下typeclassopedia约之间的差异Monad和Applicative.我能理解,没有join在Applicative.但是下面的描述看起来含糊不清,我无法弄清楚monadic计算/动作的"结果"究竟是什么意思.所以,如果我把一个值放入Maybe,这使得一个monad,这个"计算"的结果是什么?
让我们更仔细地看一下(>> =)的类型.基本的直觉是它将两个计算组合成一个更大的计算.第一个参数ma是第一个计算.但是,如果第二个论点只是一个mb,那将是无聊的; 那么计算就无法相互交互(实际上,这正是Applicative的情况).因此,(>> =)的第二个参数具有类型a - > mb:这种类型的函数,给定第一次计算的结果,可以产生第二个要运行的计算....直观地说,正是这种能力使用先前计算的输出来决定接下来运行哪些计算使Monad比Applicative更强大.应用计算的结构是固定的,而Monad计算的结构可以基于中间结果而改变.
是否有一个具体的例子说明"能够使用先前计算的输出来决定接下来要运行的计算",Applicative没有?
J. *_*son 64
我最喜欢的例子是"纯粹适用于任何一个".我们首先分析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
现在实施(<*>)是值得仔细考虑的特殊部分.它在前三种情况下执行了一些"短路" ,但在第四种情况下做了一些有趣的事情.
  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根据参数计算中嵌入的值,完全体现了结果计算的思想.
Wil*_*ess 38
Just 1描述了"计算",其"结果"是1. 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 )
所有函数都在"内部"计算中工作,没有机会打破链条.每个函数只是转换它所输入的值.从功能的角度来看,计算结构的"形状"完全"在外部".
函数可以返回一个特殊值来指示失败,但它不能导致跳过计算中的后续步骤.他们都必须以特殊的方式处理特殊价值.计算的形状不能根据接收的值改变.
对于monad,函数本身构造了他们选择的计算.
tin*_*lyx 15
这是我对@J的看法.Abrahamson的例子为什么ifA不能使用例如内部的价值(pure True).本质上,它仍然可以归结为不存在的join从功能Monad中Applicative,其结合在给出的两个不同的角度typeclassopedia解释之间的差值Monad和Applicative.
所以使用@J.亚伯拉罕森的纯粹适用范例Either:
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,没有办法将两个嵌套的ApplicativeS 折叠成一个.但这种折叠功能恰恰是join在Monad执行.所以,
ifA = join . ifA' 
ifA如果我们能够join适当地实施,将满足方程式.什么Applicative是缺少在这里是完全join的功能.换句话说,我们可以以某种方式使用前一个结果中的结果Applicative.但是在Applicative框架中这样做会涉及将返回值的类型扩展为嵌套的应用程序值,我们无法将其返回到单级应用程序值.这将是一个严重的问题,因为,例如,我们不能Applicative适当地使用S 组成函数.使用join修复问题,但非常介绍join促进Applicative了Monad.
Sas*_* NF 13
可以在apvs的类型中观察到差异的关键=<<.
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不"包含" 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这是不可能的,所以无法显示一个例子.最接近的是:
f 1 = 0
f x = x
g <$> f <$> Just 1 -- oh well, this will produce Just 0, but can't stop g
                   -- from getting applied
但下面的描述对我来说看起来很模糊,我无法弄清楚一元计算/动作的“结果”到底是什么意思。
嗯,这种模糊性在某种程度上是故意的,因为一元计算的“结果”取决于每种类型。最好的答案有点同义反复:“结果”(或结果,因为可以有多个)是实例的实现(>>=) :: Monad m => m a -> (a -> m b) -> m b调用函数参数的任何值。
那么,如果我将一个值放入
Maybe,这会生成一个 monad,那么这个“计算”的结果是什么?
单子Maybe看起来像这样:
instance Monad Maybe where
    return = Just
    Nothing >>= _ = Nothing
    Just a >>= k = k a
这里唯一有资格作为“结果”的是 的a第二个方程中的>>=,因为它是唯一被“输入”到 的第二个参数的东西>>=。
其他答案已经深入探讨了ifA两者的ifM区别,所以我想我应该强调另一个显着的区别:应用程序组成,单子不组成。对于Monads,如果你想制作一个Monad结合了两个现有s的效果的a,你必须将其中一个重写为monad转换器。相反,如果您有两个,Applicatives您可以轻松地用它们制作一个更复杂的,如下所示。(代码是从 复制粘贴的transformers。)
-- | 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)
现在,如果我们添加Constant函子/applicative:
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的其他响应中组装出“适用性” :LiftConstant
type Error e a = Lift (Constant e) a
| 归档时间: | 
 | 
| 查看次数: | 11240 次 | 
| 最近记录: |