Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monad and Functor law for Monad and Functor type class in Haskell

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

Functor Laws

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
like image 511
SmoothTraderKen Avatar asked Jan 25 '26 23:01

SmoothTraderKen


1 Answers

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
like image 107
Bartosz Milewski Avatar answered Jan 28 '26 22:01

Bartosz Milewski