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 输入内容或按回车键,而如果我从 @987654343 开始@它继续运行getLine。这种根据 values 的强度改变 done 的能力是 Monad 和 Applicative 之间的主要区别,但是您可以通过 >*> 运算符看到 Monad 可以做所有事情应用程序可以。 (它们都有return,Applicative 调用pure,它们都有(<$>) 或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 中的checkFirst 和don'tCheckFirst。)
总结
Monad 类似于 Applicative,但能够根据现有的值完全改变你正在做的事情。