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
或按回车键,而如果我以
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 ghci中,由于 getChar的Ghc bug,无法区分checkFirst
和don'tCheckFirst
。)
概述
Monad类似于Applicative,但具有基于值的完全更改所做事情的能力。
pure = return
和mf <*> ma = mf >>= \f -> liftM f ma
。 - Vitus[2,3,4] >>= \ x -> replicate x x
这样的操作确实需要>>=
的额外能力,因为每个值都用于选择生成的列表结构,而不仅仅是其中的值。单子操作更加强大,但相应地,并不像应用组合子那么常见。 - pigworker