One thing that makes it confusing is that the "popular" functions like
<*> are praxis oriented. But to understand the concepts it's easier to look at other functions first. It's also worth noting that monads stand out because they are a bit overhyped in comparison to other connected concepts.
So I will start with functors instead.
Functors offer a function (in Haskell notation)
fmap :: (Functor f) => (a -> b) -> f a -> f b. In other words you have a context
f that you can lift a function into. As you can imagine almost anything is a functor. Lists, Maybe, Either, functions, I/O, tuples, parsers... Each represents a context in which a value can appear. So you can write extremely versatile functions that work in almost any context by using
fmap or its inline variant
What other stuff do you want to do with contexts? You might want to combine two contexts. So you might want to get a generalization of
zip :: [a] -> [b] -> [(a,b)] for example like this:
pair :: (Monoidal f) => f a -> f b -> f (a,b).
But because it's even more useful in practice, the Haskell libraries instead offer
Applicative, which is a combination of
Monoidal, And also of
Unit, which just adds that you can actually put values "inside" your context with
You can write extremely generic functions by just stating these three things about the context you are working in.
Monad is just another thing you can state on top of that. What I didn't mention before is that you already have two ways to combine two contexts: You can not only
pair them, but you can also stack them, e.g. you can have a list of lists. In the I/O context, an example would be an I/O action that can read other I/O actions from a file, so you would have a type
FilePath -> IO (IO a). How can we get rid of that stacking to get an executable function
IO a? That's where
join comes in, it allows us to combine two stacked contexts of the same type. The same goes for parsers, Maybe etc. And
bind is just a more practical way to use
So a monadic context only has to offer four things and it can be used with almost all the machinery developed for I/O, for parsers, for failures etc.