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