【问题标题】:the equivalence between applicative functor and monadapplicative functor 和 monad 的等价性
【发布时间】:2012-11-12 02:17:10
【问题描述】:

人们说 monad 是应用函子的扩展,但我不这么认为。我们以应用函子为例:(<*>) :: f(a->b) -> f a -> f b

[(+3)] <*> [2,3,4]

现在,我也希望我可以做和 monad 一样的事情,这意味着我可以应用 2 个参数:一个上下文包含一个函数,另一个上下文获取一个上下文。但对于 monad,我不能。我只需要写一个像这样丑陋的函数:

[2,3,4] >>= (\x->[x+3])

是的,当然,你可以说[(+3)] 等价于[\x-&gt;(x+3)]。但至少,这个函数是在上下文中的。

最后,我在这里看不到等价或扩展。 Monad 是一种不同的风格,在另一个故事中很有用。

对不起,我的无知。

【问题讨论】:

  • 好吧,pure = returnmf &lt;*&gt; ma = mf &gt;&gt;= \f -&gt; liftM f ma
  • 应用组合子可以根据单子组合子来定义。但是像[2,3,4] &gt;&gt;= \ x -&gt; replicate x x 这样的操作确实需要&gt;&gt;= 的额外功能,因为每个值都用于选择结果列表的结构,而不仅仅是其中的值。 monad 操作严格来说更强大,但相应地,它们并不常见。

标签: haskell monads functor applicative


【解决方案1】:

如果TMonad 的一个实例,那么您可以像这样使它成为Applicative 的一个实例:

instance Functor T where
    fmap = liftM

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

liftM 定义为

liftM   :: (Monad m) => (a1 -> r) -> m a1 -> m r
liftM f m1              = do { x1 <- m1; return (f x1) }

ap 定义为

ap                :: (Monad m) => m (a -> b) -> m a -> m b
ap                =  liftM2 id

liftM2  :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2          = do { x1 <- m1; x2 <- m2; return (f x1 x2) }

因此,“monad 是 applicative functors 的扩展”,因为任何 monad 都可以成为 applicative functor。事实上,在标准库中,Monad 类不是从 Applicative 类派生的这一错误被广泛(并非普遍)认为。

【讨论】:

    【解决方案2】:
    import Control.Applicative
    

    我认为它澄清了再次定义 &lt;*&gt; 的关系,但使用 Monad:

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

    给出与&lt;*&gt;相同的结果:

    *Main> [(+3)] >*> [2,3,4]
    [5,6,7]
    *Main> [(+3)] <*> [2,3,4]
    [5,6,7]
    

    甚至

    *Main> [(+3),(*10)] <*> [2,3,4]
    [5,6,7,20,30,40]
    *Main> [(+3),(*10)] >*> [2,3,4]
    [5,6,7,20,30,40]
    

    现在变量fa 的存在以及&gt;*&gt; 定义中的最后一行是Monad 和Applicative 之间的关键区别。 在 Applicative 中,你只能在结尾处 return,而在 Monad 中,你可以用 fa 做任何你想做的事情。

    相似之处

    在 Applicative 中,你可以这样做

    getNonEmptyStringA :: IO String
    getNonEmptyStringA = (:) <$> getChar <*> getLine
    

    我们可以将其转换为 Monad 函数

    getNonEmptyStringM' = (:) `fmap` getChar >*> getLine
    

    或更典型地,

    getNonEmptyStringM :: IO String
    getNonEmptyStringM = do
        c <- getChar
        xs <- getLine
        return (c:xs)
    

    区别

    在 Monad 你可以做

    checkFirst :: IO (Maybe String)
    checkFirst = do
        c <- getChar
        if c == 'n' then return Nothing
                    else fmap Just getLine
    

    例如,

    Main> checkFirst >>= print
    qwerty
    Just "werty"
    
    Main> checkFirst >>= print
    nNothing
    

    请注意,checkFirst 改变了我输入 n 后发生的情况 - 它立即返回 Nothing,而我没有机会为 getLine 输入内容或按回车键,而如果我从 @987654343 开始@它继续运行getLine。这种根据 values 的强度改变 done 的能力是 Monad 和 Applicative 之间的主要区别,但是您可以通过 &gt;*&gt; 运算符看到 Monad 可以做所有事情应用程序可以。 (它们都有return,Applicative 调用pure,它们都有(&lt;$&gt;)fmap,因为它们都是Functor。)

    在 Applicative 中写 checkFirst 最接近的是

    don'tCheckFirst :: IO (Maybe String)
    don'tCheckFirst = check <$> getChar <*> getLine  where
       check c xs = if c == 'n' then Nothing
                    else Just (c:xs)
    

    像这样工作:

    Main> don'tCheckFirst >>= print
    nI can keep typing because it has to do the getLine anyway
    Nothing
    
    Main> don'tCheckFirst >>= print
    qwerty
    Just "qwerty"
    

    (注意:由于Windows ghc bug in getChar,您无法区分Windows ghci 中的checkFirstdon'tCheckFirst。)

    总结

    Monad 类似于 Applicative,但能够根据现有的值完全改变你正在做的事情。

    【讨论】:

      【解决方案3】:

      Haskell 中的 monad 是 Applicative plus join,即“扁平化” monad 的函数 join :: m (m a) -&gt; m a

      “应用程序”&lt;*&gt; 的类型为f (a -&gt; b) -&gt; f a -&gt; f b;如果您现在选择类型b 在同一个函子中,即b :: f c,则类型签名专门用于&lt;*&gt; :: f (a -&gt; f c) -&gt; f a -&gt; f (f c)。当你没有一元结构时,你就完了;但是,使用 monadic join 函数,您可以将结果展平,在与以前相同的 monad 中得到一些东西(而不是双堆叠的 monad)。

      【讨论】:

        猜你喜欢
        • 2012-01-29
        • 1970-01-01
        • 2011-11-05
        • 2019-12-06
        • 2019-06-30
        • 2015-07-02
        • 1970-01-01
        相关资源
        最近更新 更多