From LYAH I understand that the do notation is just syntactic sugar for monadic style; and from the wikibook I read more or less the same; so my understanding is that there can't be any do notation if there's no Monad instance.
Yet I read this definition of the Functor instance of the IO type ctor.
instance Functor IO where
fmap f action = do
result <- action
return (f result)
which is just syntactic sugar for the following, isn't it?
instance Functor IO where
fmap f action = action >>= return . f
which implies the underling assumption the IO is instance of Monad first; and this come against that fact that every Monad is a Functor and not the other way around.
In fact, I had absorbed that a Monad is "something more than" an Applicative, which is in turn "something more than" a Functor, which goes together with Applicative's definition enforcing the Functor constraint on its instances (and Monad's definition ideally requiring that its instances are Applicatives, as in don't make it a Monad if it's not an Applicative).
In other words, the code above makes me think that there would be no way to write the Functor instance for IO, if IO was not a Monad in the first place.
And now that I think about it, maybe this is just like saying that IO was created as a full-fledged Monad, and the instance above was later made just for completeness and mathematical consistency.
But I'm confused, and so I seek for help here.
A type having an instance of
Monadimplies it must have a definition ofFunctor(andApplicative), but that doesn’t mean theFunctorinstance must be defined “first”, only that both instances must exist. As long as the method implementations aren’t defined in terms of each other circularly, there’s no problem.In fact, it often makes sense to implement
Monadfor a type first, then defineApplicativeandFunctormechanically in terms ofMonadoperations, becauseMonadis more powerful, so the other instances are just restrictions of theMonadinstance:This is precisely because “a
Monadis ‘something more than’ anApplicative, which is in turn ‘something more than’ aFunctor”.It’s also worth noting that originally, the
MonadandFunctorclasses were unrelated, and theApplicativeclass (added later) was as well. There were separate equivalent functions for each, likefmap,liftA, andliftM;pureandreturn;(<*>)andap;traverseandmapM. Conventionally you would write aMonadinstance and implement the other classes in terms of that. These separate definitions still exist, but are now redundant: since the “Applicative–Monad Proposal” madeApplicativea superclass ofMonad, andFunctorofApplicative, you can always use e.g.pureinstead ofreturnortraverseinstead ofmapM, since they’re the same functions, but work in strictly more contexts. So there’s also some historical context as to why there might be separate definitions of these instances for a type.As @dfeuer points out, there are some data structures where
mapM(mapM_,forM,forM_) is more efficient thantraverse(resp.traverse_,for,for_), such asData.Vectortypes. In the specific case of vectors, I believe this is because the library can take advantage of monadic sequencing as an optimisation, to enable more streaming and allocating results in place. But they are equivalent, in thattraverseshould always produce the same result.