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 Monoid
s 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 Monoid
s -- 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
IO
anEq
instance, 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 giveIO
anEq
instance, 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 Monoid
s. 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