I have the following datatype and semigroup instance:
data Or a b =
Fst a
| Snd b deriving (Eq, Show)
instance Semigroup (Or a b) where
(<>) (Fst a) (Fst b) = Fst b
(<>) (Snd a) (Fst b) = Snd a
(<>) (Fst a) (Snd b) = Snd b
(<>) (Snd a) (Snd b) = Snd a
I want to make a monoid instance for the type above, but I do not see how to do it. If I use the following definition
instance (Monoid a, Monoid b) => Monoid (Or a b) where
mempty = (Fst mempty)
mappend = (<>)
it will work on all pairs of input to <>
, except the one where I mappend
(Fst a) <> mempty
which will evaluate to mempty
.
How do I fix this so that the mempty
is valid? It seems like it cannot be done without some new syntax or concepts since it hinges on whether the mempty is to the left or right...
There is a perfectly good (and simpler) semigroup which always takes its first argument:
newtype FirstS a = FirstS a
instance Semigroup (FirstS a) where
a <> b = a
Unfortunately, it is not a monoid because -- except for trivial choices of wrapped type -- there is no left identity of this operation. The standard First
type patches FirstS
by adding a distinguished identity element, thus:
newtype First a = First (Maybe a)
instance Semigroup (First a) where
First Nothing <> b = b
a <> First Nothing = a
a <> b = a -- this clause is exactly as before
Then it is easy to write the Monoid
instance by choosing mempty = First Nothing
. You can pull a similar trick by adding a distinguished identity element to your type:
data Or a b = Fst a | Snd b | Neither
instance Semigroup (Or a b) where
Neither <> b = b
a <> Neither = a
-- the remainder of the clauses are as before
This makes the choice of mempty = Neither
quite easy.
This pattern is so frequently useful that it actually has a newtype wrapper in semigroups
, so that you could also write this patched type using your original Or a b
type as simply Option (Or a b)
and get the Semigroup
and Monoid
instances for free.