人们说单子是合用的仿函数的延伸,但我没有看到。 让我们以适用函子的例子: (<*>) :: f(a->b) -> fa -> fb
[(+3)] <*> [2,3,4]
现在,我也希望我可以做同样的事情单子,这意味着我可以申请2个参数:上下文包含一个函数,而另一个方面,以获得上下文。 但单子,我不能。 所有我需要的是写一个丑陋的功能是这样的:
[2,3,4] >>= (\x->[x+3])
是的,当然,你可以说[(+3)]
等同于[\x->(x+3)]
。 但至少,这个功能是在上下文中。
最后,我没有看到等价或扩展在这里。 单子是不同的风格和有用的另一个故事。
对不起,我的无知。
如果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
不从类派生Applicative
。
import Control.Applicative
我认为它明确的关系来定义<*>
再次,但使用单子:
(>*>) :: 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
和最后一行中的定义>*>
是单子和应用型的主要区别 。 在应用型,你只能return
在最后的东西,而在一个单子,你可以做任何你喜欢的f
和a
。
相似
在应用型,你可以做
getNonEmptyStringA :: IO String
getNonEmptyStringA = (:) <$> getChar <*> getLine
我们可以转化为Monad的功能
getNonEmptyStringM' = (:) `fmap` getChar >*> getLine
或者更一般地,
getNonEmptyStringM :: IO String
getNonEmptyStringM = do
c <- getChar
xs <- getLine
return (c:xs)
区别
在单子你可以做
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
。 这改变什么大干快上的价值观的力量完成的能力是单子和应用型的主要区别,但你可以通过看>*>
操作符单子做的一切应用型一样。 (他们都有return
,其中应用型呼叫pure
,和他们都有(<$>)
或fmap
,因为他们都是仿函数)。
你可以开始编写最接近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"
(注:你不能告诉之间的区别checkFirst
和don'tCheckFirst
因为在ghci中在Windows中, 在getchar函数的Windows GHC的错误 。)
摘要
单子是喜欢应用型但要彻底改变您是根据什么样的价值观有做什么的能力。
Haskell中单子是一个应用型加join
,即功能“平坦”的单子, join :: m (ma) -> ma
。
在“应用型应用” <*>
具有类型f (a -> b) -> fa -> fb
; 如果你现在选择的类型b
是在同一个函子,即b :: fc
,类型签名专注于<*> :: f (a -> fc) -> fa -> f (fc)
当你没有一个一元结构,你在这里做; 但是,在使用单子join
功能,您可以拼合结果,得到在同一单子东西之前(而不是双叠单子)。