the equivalence between applicative functor and mo

2019-03-22 02:29发布

People say monads are an extension of applicative functors, but I don't see that. Let's take an example of applicative functor: (<*>) :: f(a->b) -> f a -> f b

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

Now, I also expect I can do the same thing as monad, it means I can apply 2 parameters: a context contains a function, and another context to get a context. But for monad, I can't. All I need is to write an ugly function like this:

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

Yes, of course, you can say that [(+3)] is equivalent to [\x->(x+3)]. But at least, this function is in context.

Finally, I don't see the equivalence or extension here. Monad is a different style and useful in another story.

Sorry for my ignorance.

3条回答
The star\"
2楼-- · 2019-03-22 03:02
import Control.Applicative

I think it clarifies the relationship to define <*> again, but using a Monad:

(>*>) :: Monad m => m (a -> b) -> m a -> m b
mf >*> ma = do
   f <- mf
   a <- ma
   return (f a)

Giving the same results as <*>:

*Main> [(+3)] >*> [2,3,4]
[5,6,7]
*Main> [(+3)] <*> [2,3,4]
[5,6,7]

or even

*Main> [(+3),(*10)] <*> [2,3,4]
[5,6,7,20,30,40]
*Main> [(+3),(*10)] >*> [2,3,4]
[5,6,7,20,30,40]

Now the presence of the variables f and a and the last line in the definition of >*> is the key difference between Monad and Applicative. In Applicative, you can only return something at the end, whereas in a Monad, you can do whatever you like with f and a.

Similarities

In Applicative, you could do

getNonEmptyStringA :: IO String
getNonEmptyStringA = (:) <$> getChar <*> getLine

Which we could translate into Monad functions as

getNonEmptyStringM' = (:) `fmap` getChar >*> getLine

or more typically,

getNonEmptyStringM :: IO String
getNonEmptyStringM = do
    c <- getChar
    xs <- getLine
    return (c:xs)

Difference

In Monad you could do

checkFirst :: IO (Maybe String)
checkFirst = do
    c <- getChar
    if c == 'n' then return Nothing
                else fmap Just getLine

For example,

Main> checkFirst >>= print
qwerty
Just "werty"

Main> checkFirst >>= print
nNothing

Notice that checkFirst changed what happened after I typed the n - it returned Nothing straight away without giving me a chance to type something for getLine or to press enter, whereas if I start with q it carries on to run getLine. This ability to change what gets done on the strength of the values is the key difference between Monad and Applicative, but you can see with the >*> operator that Monad does everything Applicative does. (They both have return, which Applicative calls pure, and they both have (<$>) or fmap because they're both Functors.)

The closest you can get to writing checkFirst in Applicative is

don'tCheckFirst :: IO (Maybe String)
don'tCheckFirst = check <$> getChar <*> getLine  where
   check c xs = if c == 'n' then Nothing
                else Just (c:xs)

Which works like this:

Main> don'tCheckFirst >>= print
nI can keep typing because it has to do the getLine anyway
Nothing

Main> don'tCheckFirst >>= print
qwerty
Just "qwerty"

(Note: you can't tell the difference between checkFirst and don'tCheckFirst in ghci in windows, because of a Windows ghc bug in getChar.)

Summary

Monad is like Applicative but with the ability to completely change what you're doing based on what values there are.

查看更多
迷人小祖宗
3楼-- · 2019-03-22 03:08

A monad in Haskell is an Applicative plus join, i.e. a function to "flatten" the monad, join :: m (m a) -> m a.

The "Applicative application" <*> has type f (a -> b) -> f a -> f b; if you now choose the type b to be in the same Functor, i.e. b :: f c, the type signature specializes to <*> :: f (a -> f c) -> f a -> f (f c). When you don't have a monadic structure, you're done here; however, using the monadic join function, you can flatten the result, getting something in the same monad as before (instead of a double stacked monad).

查看更多
Summer. ? 凉城
4楼-- · 2019-03-22 03:09

If T is an instance of Monad, then you can make it an instance of Applicative like this:

instance Functor T where
    fmap = liftM

instance Applicative T where
    pure = return
    (<*>) = ap

liftM is defined as

liftM   :: (Monad m) => (a1 -> r) -> m a1 -> m r
liftM f m1              = do { x1 <- m1; return (f x1) }

ap is defined as

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) }

So, "monads are an extension of applicative functors" in the sense that any monad can be made into an applicative functor. Indeed, it is widely (not universally) considered a bug in the standard library that class Monad does not derive from class Applicative.

查看更多
登录 后发表回答