Stack Overflow Asked on December 2, 2021
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 Applicative
s, 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 Monad
implies it must have a definition of Functor
(and Applicative
), but that doesn’t mean the Functor
instance 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 Monad
for a type first, then define Applicative
and Functor
mechanically in terms of Monad
operations, because Monad
is more powerful, so the other instances are just restrictions of the Monad
instance:
-- These work for any T with a Monad instance.
instance Functor T where
fmap f x = do
x' <- x
return (f x')
instance Applicative T where
pure = return
f <*> x = do
f' <- f
x' <- x
return (f' x')
This is precisely because “a Monad
is ‘something more than’ an Applicative
, which is in turn ‘something more than’ a Functor
”.
It’s also worth noting that originally, the Monad
and Functor
classes were unrelated, and the Applicative
class (added later) was as well. There were separate equivalent functions for each, like fmap
, liftA
, and liftM
; pure
and return
; (<*>)
and ap
; traverse
and mapM
. Conventionally you would write a Monad
instance and implement the other classes in terms of that. These separate definitions still exist, but are now redundant: since the “Applicative–Monad Proposal” made Applicative
a superclass of Monad
, and Functor
of Applicative
, you can always use e.g. pure
instead of return
or traverse
instead of mapM
, 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 than traverse
(resp. traverse_
, for
, for_
), such as Data.Vector
types. 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 that traverse
should always produce the same result.
Answered by Jon Purdy on December 2, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP