When I compare the binary operations of the Applicative and Monad type class
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
(=<<) :: Monad m => (a -> m b) -> m a -> m b
two differences become apparent:
ap
expects a normal, pure function, whereas bind
expects a monadic action, which must return a monad of the same typeap
the sequence of actions is determined by the applicative, whereas with bind
the monadic action can determine the control flowSo monads give me additional expressive power. However, since they no longer accept normal, pure functions, this expressiveness seems to come at the expense of code reuse.
My conclusion might be somewhat naive or even false, since I have merely little experience with Haskell and monadic computations. Any light in the dark is appreciated!
Code reuse is only an advantage if you can reuse code to do what you actually want.
GHCi> putStrLn =<< getLine
Sulfur
Sulfur
GHCi>
Here, (=<<)
picks the String
result produced in an IO
context by getLine
and feeds it to putStrLn
, which then prints said result.
GHCi> :t getLine
getLine :: IO String
GHCi> :t putStrLn
putStrLn :: String -> IO ()
GHCi> :t putStrLn =<< getLine
putStrLn =<< getLine :: IO ()
Now, in the type of fmap
/(<$>)
...
GHCi> :t (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
... it is perfectly possible for b
to be IO ()
, and therefore nothing stops us from using putStrLn
with it. However...
GHCi> putStrLn <$> getLine
Sulfur
GHCi>
... nothing will be printed.
GHCi> :t putStrLn <$> getLine
putStrLn <$> getLine :: IO (IO ())
Executing an IO (IO ())
action won't execute the inner IO ()
action. To do that, we need the additional power of Monad
, either by replacing (<$>)
with (=<<)
or, equivalently, by using join
on the IO (IO ())
value:
GHCi> :t join
join :: Monad m => m (m a) -> m a
GHCi> join (putStrLn <$> getLine)
Sulfur
Sulfur
GHCi>
Like chi, I also have trouble understanding the premises of your question. You seem to expect that one of Functor
, Applicative
and Monad
will turn out to be better than the others. That is not the case. We can do more things with Applicative
than with Functor
, and even more with Monad
. If you need the additional power, use a suitably powerful class. If not, using a less powerful class will lead to simpler, clearer code and a broader range of available instances.