I am a somewhat experienced functional programmer, but I have always been confused by the statement “A monad is just a monoid in the category of endofunctors” and the actual implementation of monads/functors in Haskell or other functional languages.
According to the Haskell Wiki, https://wiki.haskell.org/Functor
class Functor f where
fmap :: (a -> b) -> f a -> f b
(<$) :: a -> f b -> f a
Functors must preserve identity morphisms
fmap id = id
Functors preserve composition of morphisms
fmap (f . g) == fmap f . fmap g
I understand this well, so no further explanation needed; however, when we uses the monad operator that is >>=, now we have Monad implementation and Monad laws
https://wiki.haskell.org/Monad_laws
Here, I’m not questioning each implementation and rule. What I’m wondering is if we say “every Monad is an endofunctor because a monad is just a monoid in the category of endofunctors”, I expect a monad instance to satisfy the functor law, but in reality, it does not.
Or, although a monad is an endofunctor, the instance has 2 morphisms which are >>= and fmap. Do these two morphisms have independent laws?
I understand that the functor laws come from category theory and that a monad satisfies Monoid laws. But again, if a monad is an endofunctor, I don’t understand why it does not satisfy functor laws.
For intance,
main :: IO ()
main = do
print $ [1, 2, 3] >>= \a -> [a] // [2, 4, 6]
This code does not work if I change
print $ [1, 2, 3] >>= id // error "functor law" is not satisfied
To begin with, there are two equivalent definitions of a monad. The first corresponds directly to the "monoid of endofunctors":
class Functor m => Monad m where return :: a -> m a join :: m (m a) -> m a
Here, we explicitly assume that m is an endofunctor. The monoidal "tensor product" of endofunctors is given by their composition. So join is an arrow from the "square" of m to m; it defines "multiplication" in our monoid. return is an arrow from the identity endofunctor to m; it defines monoidal unit. There are three monoidal laws in terms of join and return corresponding to the left/right unit and associativity.
The Monad definition used in Haskell uses bind >>= instead of join. A monad defined using bind is automatically an endofunctor, with fmap defined as:
liftM :: Monad m => (a -> b) -> m a -> m b liftM f ma = ma >>= return . f
The (relatively simple) monoidal laws from the first definition translate into the following laws:
return a >>= k = k a ma >>= return = ma ma >>= (\x -> k x >>= h) = (ma >>= k) >>= h
Functor laws for liftM follow from these laws. For instance, you can derive the composition law using these steps:
(liftM f . liftM g) ma = liftM f (ma >>= (return . g)) = (ma >>= (return . g)) >>= return . f = ma >>= (\x -> ((return (g x)) >>= return . f)) = ma >>= (\x -> return (f (g x))) = liftM (f . g) ma
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With