2

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 
duplode
  • 33,731
  • 7
  • 79
  • 150
sevo
  • 4,559
  • 1
  • 15
  • 31
  • Incidentally, there's no such thing as _type inequality_ in Haskell. There's just type equality `a ~ ()`, and like with all constraints, it is assumed that this could in principle be true even if the compiler can't proove it – so you can never negate constraints. In fact is _is_ true here, `a ~ ()` is precisely what the compiler infers for that `mzero`! – leftaroundabout Feb 19 '15 at 14:09

1 Answers1

11

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.

chi
  • 111,837
  • 3
  • 133
  • 218