The Monad
typeclass can be defined in terms of return
and (>>=)
. However, if we already have a Functor
instance for some type constructor f
, then this definition is sort of 'more than we need' in that (>>=)
and return
could be used to implement fmap
so we're not making use of the Functor
instance we assumed.
In contrast, defining return
and join
seems like a more 'minimal'/less redundant way to make f
a Monad
. This way, the Functor
constraint is essential because fmap
cannot be written in terms of these operations. (Note join
is not necessarily the only minimal way to go from Functor
to Monad
: I think (>=>)
works as well.)
Similarly, Applicative
can be defined in terms of pure
and (<*>)
, but this definition again does not take advantage of the Functor
constraint since these operations are enough to define fmap
.
However, Applicative f
can also be defined using unit :: f ()
and (>*<) :: f a -> f b -> f (a, b)
. These operations are not enough to define fmap
so I would say in some sense this is a more minimal way to go from Functor
to Applicative
.
Is there a characterization of Monad
as fmap
, unit
, (>*<)
, and some other operator which is minimal in that none of these functions can be derived from the others?
(>>=)
does not work, since it can implementa >*< b = a >>= (\ x -> b >>= \ y -> pure (x, y))
wherepure x = fmap (const x) unit
.- Nor does
join
sincem >>= k = join (fmap k m)
so(>*<)
can be implemented as above. - I suspect
(>=>)
fails similarly.