人们说monad是应用函子的延伸,但我没有看到。我们来看一个applicative functor的例子:(<*>) :: 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是另一种风格,在另一个故事中很有用。
对不起我的无知。
答案 0 :(得分:12)
如果T
是Monad
的实例,那么您可以将其设为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是应用函子的扩展”,因为任何monad都可以被制作成一个applicative functor。实际上,标准库中广泛(并非普遍)认为类Monad
不是从类Applicative
派生出来的。
答案 1 :(得分:9)
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]
现在变量f
和a
的存在以及>*>
定义中的最后一行是Monad和Applicative之间的键差异。
在Applicative中,最后只能return
一些内容,而在Monad中,您可以使用f
和a
执行任何操作。
在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和>*>
运算符一切都是适用的。 (他们都有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中checkFirst
和don'tCheckFirst
之间的区别。)
Monad就像是Applicative,但能够根据有什么价值完全改变你正在做的事情。
答案 2 :(得分:6)
Haskell中的monad是一个Applicative加join
,即“展平”monad的函数join :: m (m a) -> m a
。
“适用应用”<*>
的类型为f (a -> b) -> f a -> f b
;如果您现在选择类型b
属于同一个Functor,即b :: f c
,则类型签名专门用于<*> :: f (a -> f c) -> f a -> f (f c)
。当你没有monadic结构时,你就完成了;但是,使用monadic join
函数,你可以展平结果,获得与之前相同的monad(而不是双堆叠monad)。