Automatically deriving Generic for every algebraic data type
Ryan Scott
ryan.gl.scott at gmail.com
Thu Feb 4 14:20:00 UTC 2016
I'm a pretty solid -1 on this idea.
On a general level, I'm opposed to the idea of deriving typeclasses
without the programmer opting in. Most typeclasses express operations
that your datatype must support, and in the case of Generic(1), it
mandates that users can convert between values of your datatype and an
isomorphic representation type. As Oleg noted, this completely
destroys encapsulation for datatypes whose constructors you don't want
to export.
Another issue is the nontrivial cost of automatic derivation.
Typeclasses can be surprisingly expensive to derive. About a year ago,
Reid Barton measured the amount of increase in code size that each
derivable typeclass contributes [1], and Generic was far and away the
most expensive one. There's also the issue that deriving Generic on
large datatypes can drastically increase the amount of memory needed
during compilation [2].
Yet another problem is that deriving Generic1 simply doesn't work for
every datatype. Not only does deriving Generic1 not work with
sophisticated language features (e.g., -XExistentialQuantification
[3]) by design, but there are still a number of outstanding bugs that
prevent legitimate Generic1 from being deriving automatically. For
example, the following datatype:
newtype Compose (f :: k1 -> *) (g :: k2 -> k1) (a :: k2) = Compose (f (g a))
chokes when attempting to derive Generic1 automatically [4]. You have
to hack around this by using -XStandaloneDeriving with a
programmer-specified instance context.
One could imagine a compromise in which Generic(1) would not be
derived for datatypes for which attempting to derive those classes
yields an error. But I would argue that this is very undesirable,
since programmers would just expect every datatype to work out of the
box with Generic(1), only to find "Cannot find instance Generic1
Compose"-like errors when they try using Generic1 operations on
datatypes that trip the bug mentioned in [4].
There are exceptions to the don't-derive-typeclasses-automatically
rule, with Typeable being a notable example [5]. But deriving Typeable
has the benefit that (1) it doesn't impose many requirements on your
datatype (other than you can get a TypeRep for it), (2) it's cheap to
derive (see the table in [1]), and (3) it works on literally every
datatype.
Ryan S.
-----
[1] https://ghc.haskell.org/trac/ghc/ticket/9557#comment:8
[2] https://ghc.haskell.org/trac/ghc/ticket/5642
[3] https://ghc.haskell.org/trac/ghc/ticket/10514
[4] https://ghc.haskell.org/trac/ghc/ticket/10524#comment:16
[5] https://ghc.haskell.org/trac/ghc/ticket/8950
More information about the ghc-devs
mailing list