According to the Typeclassopedia (among other sources), Applicative
logically belongs between Monad
and Pointed
(and thus Functor
) in the type class hierarchy, so we would ideally have something like this if the Haskell prelude were written today:
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Pointed f where
pure :: a -> f a
class Pointed f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
-- either the traditional bind operation
(>>=) :: (m a) -> (a -> m b) -> m b
-- or the join operation, which together with fmap is enough
join :: m (m a) -> m a
-- or both with mutual default definitions
f >>= x = join ((fmap f) x)
join x = x >>= id
-- with return replaced by the inherited pure
-- ignoring fail for the purposes of discussion
(Where those default definitions were re-typed by me from the explanation at Wikipedia, errors being my own, but if there are errors it is at least in principle possible.)
As the libraries are currently defined, we have:
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b
and:
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap :: (Monad m) => m (a -> b) -> m a -> m b
Note the similarity between these types within each pair.
My question is: are liftM
(as distinct from liftA
) and ap
(as distinct from <*>
), simply a result of the historical reality that Monad
wasn't designed with Pointed
and Applicative
in mind? Or are they in some other behavioral way (potentially, for some legal Monad
definitions) distinct from the versions that only require an Applicative
context?
If they are distinct, could you provide a simple set of definitions (obeying the laws required of Monad
, Applicative
, Pointed
, and Functor
definitions described in the Typeclassopedia and elsewhere but not enforced by the type system) for which liftA
and liftM
behave differently?
Alternatively, if they are not distinct, could you prove their equivalence using those same laws as premises?