首页 文章

应用函子和monad之间的等价性

提问于
浏览
3

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

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

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

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

是的,当然,你可以说 [(+3)] 相当于 [\x->(x+3)] . 但至少,这个功能是在上下文中 .

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

对不起我的无知 .

3 回答

  • 9

    如果 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) }
    

    所以,"monads are an extension of applicative functors"在某种意义上说任何monad都可以成为一个应用程序 . 实际上,它被广泛(不是普遍地)认为是标准库中的一个错误,类 Monad 不是从类 Applicative 派生的 .

  • 6
    import Control.Applicative
    

    我认为它澄清了再次定义 <*> 的关系,但使用Monad:

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

    给出与 <*> 相同的结果:

    *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 的存在以及 >*> 定义中的最后一行是Monad和Applicative之间的 key difference . 在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 或按Enter键,而如果我从 q 开始它继续运行 getLine . 这种改变在值的强度上完成的能力是Monad和Applicative之间的关键区别,但你可以看到 >*> 运算符,Monad完成了Applicative所做的一切 . (它们都有 return ,Applicative调用 pure ,它们都有 (<$>)fmap ,因为它们都是Functors . )

    你最接近在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,但有能力根据有什么 Value 完全改变你正在做的事情 .

  • 12

    Haskell中的monad是一个Applicative加 join ,即"flatten" monad的函数, join :: m (m a) -> m a .

    "Applicative application" <*> 的类型为 f (a -> b) -> f a -> f b ;如果您现在选择类型 b 在同一个Functor中,即 b :: f c ,则类型签名专门用于 <*> :: f (a -> f c) -> f a -> f (f c) . 当你不在这里做的时候;但是,使用monadic join 函数,你可以展平结果,获得与之前相同的monad中的某些东西(而不是双堆叠monad) .

相关问题