20

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.

mergeconflict
  • 8,156
  • 34
  • 63

2 Answers2

18

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.

augustss
  • 22,884
  • 5
  • 56
  • 93
8

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.

sdcvvc
  • 25,343
  • 4
  • 66
  • 102
  • 1
    Well, as ad-hoc as `deriving` may be, they've continued to expand its power. Again, I know there are alternative mechanisms for deriving `Monoid`; instead I'm asking specifically why `DeriveFunctor` (and others) exists, while `DeriveMonoid` (and others) does not. Is it just arbitrary? – mergeconflict Jun 23 '12 at 03:47
  • 2
    In the end it's arbitrary, but the proposed extension seems to be much less useful. A special language feature to serve most functors seems good to me. A special language feature to serve product monoids seems to little to warrant addition. You would need to think about scope (if `Monoid` then what other classes?). Note that useful monoids often use newtype wrappers anyway. So you write `data T = T (Sum Integer) (Endo Char)` to use that form of deriving, which is bit ugly to use (need to match on nested constructors). – sdcvvc Jun 23 '12 at 04:33
  • 2
    There are plenty of useful monoids that are sum types, but this implementation can't derive any of their instances. =( – Louis Wasserman Jun 23 '12 at 13:55
  • 4
    @mergeconflict there is only one sensible definition of Functor for a given type. Any other valid instance is equivalent. With Foldable and Traversable, there is a slightly less satisfying story about how there is an implied best practice in the ordering of fields. All of these work over multiple constructors and don't have a huge pile of alternate definitions like Monoid. If you want a tuple of monoids, just use (,,,,). – Edward Kmett Jun 26 '12 at 23:55