[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