[GHC] #13065: Prohibit user-defined Generic and Generic1 instances

GHC ghc-devs at haskell.org
Wed Jan 4 18:04:58 UTC 2017


#13065: Prohibit user-defined Generic and Generic1 instances
-------------------------------------+-------------------------------------
        Reporter:  dfeuer            |                Owner:
            Type:  feature request   |               Status:  new
        Priority:  normal            |            Milestone:  8.4.1
       Component:  Compiler          |              Version:  8.0.1
      Resolution:                    |             Keywords:  Generics
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  Other             |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by RyanGlScott):

 I believe I support the general idea in this proposal, although I'd like
 to add some clarifications to the reasons you've listed.

 Clarification number one is that hand-written `Generic` instances are
 already forbidden as of GHC 7.10, provided you have the `Safe` extension
 enabled.

 -----

 Replying to [ticket:13065 dfeuer]:
 > User-defined `Generic` and `Generic1` instances are problematic.

 Agreed. `Generic` is truly unique in that any given `Generic` instance can
 and should be determined solely by the algebraic structure of the
 datatype. Any other use of `Generic` is an abuse of its intended purpose,
 in my opinion.

 > === They are susceptible to breakage ===
 > Some details of the classes may change between GHC versions, and indeed
 have done so in the past. User-defined instances are likely to break in
 the face of various such "internal" changes. This is one reason why
 `Data.Sequence`, for example, does not have a `Generic` instance.

 It's true that we've changed the internal details of `GHC.Generics`
 before, but believe me when I say that we tried to preserve backwards
 compatibility as much as possible :) There are a good many other breaking
 changes we //could// make, but we haven't yet (see #7492). With CPP, it's
 actually quite feasible to maintain fancy type-level generics hackery all
 the way back to GHC 7.2.

 That being said, I would argue that `Data.Sequence` shouldn't have a
 `Generic` instance for a different reason: it's an abstract type. A
 `Generic` instance necessarily leaks every implementation detail about
 your datatype, so having a `Generic` instance for an abstract type is
 nonsense.

 > === They require potentially-expensive consistency checks ===
 > GHC cannot assume that every type has at most one `Generic` and
 `Generic1` instance, so it needs to look for possible alternatives at
 instance resolution time. According to Simon (and maybe also Simon), this
 may be partly responsible for the performance regressions seen in
 Phab:D2899.

 If we're pursing this goal in the name of compiler performance (see also
 #12731, which I believe is very relevant here), we need to be careful in
 stating our goals. As I stated
 [https://ghc.haskell.org/trac/ghc/ticket/12731#comment:3 here], it
 wouldn't be enough to disallow hand-written `Generic` instances. You'd
 also need to check that there's only one `Generic` instance per datatype,
 lest you end up with a scenario like this (which is currently possible in
 GHC 8.0):

 {{{#!hs
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE StandaloneDeriving #-}

 import GHC.Generics

 data Foo a = Foo a
 deriving instance {-# OVERLAPPABLE #-} Generic (Foo a)
 deriving instance {-# OVERLAPPING  #-} Generic (Foo Int)
 }}}

 > === Downsides ===
 > Prohibiting user-defined instances does have some costs. Suppose a type
 was originally defined concretely, exposing its constructors and a
 `Generic` instance. The implementer may decide later to make the type
 abstract, and export pattern synonyms to retain the same interface. But
 the `Generic` instance will either change or disappear. Someone relying on
 that instance could be in trouble. If the instance disappears, they'll be
 forced to write code by hand that they didn't need to before. If it
 changes, their code may change its behavior unexpectedly.

 I don't follow this argument. If the definition of a datatype changes,
 then either it's `Generic` instance must change, or you should just remove
 the `Generic` instance altogether. Full stop. Anything else would be a
 lie.

 -----

 To contribute one more "downside" of this proposal, drawing from my own
 experience, there is only one scenario where I've found hand-written
 `Generic` instances to be necessary: backwards compatibility. In old
 versions of GHC, there were numerous bugs that prevented you from deriving
 `Generic` instances for sufficiently polykinded datatypes, forcing you to
 resort to manual implementation. But these bugs have all been squashed,
 AFAICT, so I don't see any reason why we'd need to worry about this going
 forward.

 In fact, manually implementing `Generic` instances is precisely how the
 `generic-deriving` library allows you to define `Generic` instances via
 Template Haskell (in case `deriving Generic` is buggy on an old GHC). So
 if we did implement this proposal, that part of `generic-deriving` would
 no longer work. But that's a sacrifice I think I'd be OK with.

 -----

 If we do decide to press on with this propsal, let's not let this Trac
 ticket be the end of the discussion. Let's advertise this change as a
 concrete GHC proposal first. I bet there's someone out there who is
 relying on hand-written `Generic` instances who hasn't spoken up, and I'd
 hate to break their code without warning.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/13065#comment:3>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list