
#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.
=== 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
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. 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.
=== 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
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) }}} 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