I have trouble understanding how guard
works. Why does it type check? Isn't mzero
capable of returning some m a
where a /= ()
?
guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero
mzero :: m a
Yes, mzero
is capable of returning some m a
where a /= ()
. But it's also capable of returning m ()
. guard
uses it in this second case.
It's similar to this:
n :: Int
n = 5
5
can be a Float
or Double
, but can also be an Int
. The compiler chooses the needed interpretation of 5
during type checking.
Similarly, the compiler chooses the right type for mzero
in the original example during type checking. More precisely, it sees that a m ()
is needed, so it chooses that type.
The important bit here is that
mzero :: MonadPlus m => m a
actually means
mzero :: forall m a . MonadPlus m => m a
which states that the caller of mzero
gets to choose the actual values for m
and a
(as long as m
is a MonadPlus
). Hence, the caller can choose a=()
to make things type check. This choice can be made by the user through a type annotation, otherwise the compiler will try to infer the correct choice during type checking.