The third monoid law states that e <> (e <> e) = (e <> e) <> e (to use the infix operator it is easier for mappend ), and not for e <> (e <> e) == (e <> e) <> e (note = vs. == ).
It expresses equivalence - in fact, expressing that mappend should be associative - without requiring all Monoid also instances of the Eq , where == comes from.
Another way of saying this is: it expresses a high-level idea about the behavior of the mappend function without presenting a valid Haskell code that should evaluate anything in particular.
Some types that Monoid - for example, [()] , for example, also have an Eq instance. But some (for example, an instance of IO () here) do not, and everything is in order.
Sidenote: Actually, it makes no sense to give an IO instance of Eq , because it cannot determine if a particular IO () equivalent to another IO () . Is putStrLn "3" equal to " print 3 ? Both of them have the same observable effects, but how, in fact, can this determine the runtime in the general case? And you cannot say" they are equivalent if they produce same values, "because then the returned type == must be an IO Bool , and this is not a valid signature for Eq . Therefore, we simply do not provide an IO instance of Eq , and this is great - I canβt come up with a reasonable example of when such instance will be useful.
Also note that β IO β does not have a Monoid instance (this is not the type, anyway). mappend that you use comes from the instance for Monoid a => Monoid (IO a) - that is, IO recipes for creating types that are Monoid s themselves. IO Monoid instance is simply βcopiesβ in the base Monoid instance.
source share