20
votes

GHC has a few language flags, such as DeriveFunctor, DeriveDataTypeable etc., which enable compiler generation of derived instances for type classes other than those allowed in Haskell 98. This especially makes sense for something like Functor, where the laws of that class dictate an obvious, "natural" derived instance.

So why not for Monoid? It seems like for any data type with a single data constructor:

data T = MkT a b c ...

one could mechanically produce a Monoid instance (excuse the pseudocode):

instance (Monoid a, Monoid b, Monoid c, ...) => Monoid T where
  mempty =
    MkT mempty mempty mempty ...
  mappend (MkT a1 b1 c1 ...) (MkT a2 b2 c2 ...) =
    MkT (mappend a1 a2) (mappend b1 b2) (mappend c1 c2) ...

I'm aware that the derive package provides this, but my question specifically is whether there's a reason why GHC does not.

2
@sdcvvc: This seems like a valid answer to the question. Perhaps you should post it as such?Tikhon Jelvis
There's usually only sensible way to make a Functor instance. The same is not true about Monoid.augustss
@augustss can you elaborate a bit? this is probably the sort of answer i'm looking for.mergeconflict

2 Answers

17
votes

It's really an arbitrary decision to not be able to derive Monoid, but monoids are also very general so there is typically many ways to make a type a monoid. Here's an example:

data T = A | B | C deriving (Eq, Ord, Enum)

type Mon a = (a, a -> a -> a)

m1, m2, m3, m4 :: Mon T
m1 = (A, max)
m2 = (C, min)
m3 = (A, \ x y -> toEnum $ (fromEnum x + fromEnum y) `rem` 3)
m4 = (B, f4)
f4 A _ = A
f4 B x = x
f4 C _ = C

This shows four reasonable ways to make T a monoid (with Mon containing the unit and the binary operation). The first is the monoid from taking the maximum, the second the monoid from taking the minimum, the third the monoid from modulo 3 arithmetic, and the fourth is the monoid used for the Ordering type. Nothing really stands out as the natural way.

8
votes

You could ask the same for Num and some other classes. It would be inconsequential: All other standard derivations work for data types with multiple constructors.

As a replacement, you can use newtype deriving newtype T = MkT (a,b,c) deriving Monoid.

Similar extension: you can make the empty datatype an instance of almost every type class.

The deriving clause was always ad-hoc and inconvenient part of the Haskell, because it worked only for predefined classes. Adding even more ad-hoc extensions would complicate language. Instead, GHC has recently got support for generic deriving.