Looking at this IO code:
Prelude> let e = return () :: IO ()
Prelude> e `mappend` e
Prelude> let y = e `mappend` e
Prelude> :t y
y :: IO ()
EDIT Apparently, as I understand, IO has a Monoid instance.
However, shouldn't the following evaluate to true, in order to obey the Monoid third law?
Prelude> e `mappend` (e `mappend` e) == (e `mappend` e) `mappend` e
<interactive>:14:1: error:
* No instance for (Eq (IO ())) arising from a use of `=='
* In the expression:
e `mappend` (e `mappend` e) == (e `mappend` e) `mappend` e
In an equation for `it':
it = e `mappend` (e `mappend` e) == (e `mappend` e) `mappend` e
The third monoid law states that e <> (e <> e) = (e <> e) <> e (to use the easier-to-type infix operator for mappend), not that e <> (e <> e) == (e <> e) <> e (note the = vs. ==).
It's expressing an equivalence -- really, expressing that mappend should be associative -- not demanding all Monoids must also be instances of the Eq typeclass, where the == comes from.
Another way of saying this: it's expressing a high-level idea about the behavior of the mappend function, not presenting valid Haskell code that should evaluate to anything in particular.
Some types that are Monoids -- like [()], for instance -- also happen to have an Eq instance. But some (like the IO () instance here) don't, and that's okay.
Sidenote: It doesn't really make sense to give
IOanEqinstance, because it's nigh on impossible to determine if a certainIO ()is equivalent to anotherIO (). IsputStrLn "3""equal" toprint 3? They both have the same observable effects, but how on earth could the runtime determine that in the general case? And you can't say "they're equivalent if they produce the same values," because then the return type of==would have to be anIO Bool, and that's not the right signature forEq. So we just don't giveIOanEqinstance, and that's fine -- I can't come up with a reasonable example of when such an instance would be useful.
Also note that "IO" doesn't have a Monoid instance (it's the wrong kind, anyway). The mappend that you're using comes from the instance for Monoid a => Monoid (IO a) — that is, IO recipes to produce types which are themselves Monoids. IO's Monoid instance just "piggy-backs" on the underlying Monoid instance.
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