适用函子和单子之间的等价(the equivalence between applicative f

2019-08-03 03:25发布

人们说单子是合用的仿函数的延伸,但我没有看到。 让我们以适用函子的例子: (<*>) :: f(a->b) -> fa -> fb

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

现在,我也希望我可以做同样的事情单子,这意味着我可以申请2个参数:上下文包含一个函数,而另一个方面,以获得上下文。 但单子,我不能。 所有我需要的是写一个丑陋的功能是这样的:

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

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

最后,我没有看到等价或扩展在这里。 单子是不同的风格和有用的另一个故事。

对不起,我的无知。

Answer 1:

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



Answer 2:

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]

现在变量的存在fa和最后一行中的定义>*>是单子和应用型的主要区别 。 在应用型,你只能return在最后的东西,而在一个单子,你可以做任何你喜欢的fa

相似

在应用型,你可以做

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"

(注:你不能告诉之间的区别checkFirstdon'tCheckFirst因为在ghci中在Windows中, 在getchar函数的Windows GHC的错误 。)

摘要

单子是喜欢应用型但要彻底改变您是根据什么样的价值观有做什么的能力。



Answer 3:

Haskell中单子是一个应用型加join ,即功能“平坦”的单子, join :: m (ma) -> ma

在“应用型应用” <*>具有类型f (a -> b) -> fa -> fb ; 如果你现在选择的类型b是在同一个函子,即b :: fc ,类型签名专注于<*> :: f (a -> fc) -> fa -> f (fc) 当你没有一个一元结构,你在这里做; 但是,在使用单子join功能,您可以拼合结果,得到在同一单子东西之前(而不是双叠单子)。



文章来源: the equivalence between applicative functor and monad