Skip to content

Generic type class has type family; leads to big dep_finsts

While trying to determine a good explanation for simonpj's question in D2607, I noticed that dep_finsts was a lot larger than I would have ordinarily expected it to be: it included many modules that did not have the TypeFamilies extension enabled for them. For example, for one module in Cabal, here's "family instance modules":

family instance modules: Distribution.Compat.Semigroup
                         Distribution.Compiler Distribution.ModuleName Distribution.Package
                         Distribution.Simple.Compiler Distribution.System
                         Distribution.Utils.ShortText Distribution.Verbosity
                         Distribution.Version Language.Haskell.Extension Control.Applicative
                         Data.Complex Data.Either Data.Functor.Const Data.Functor.Identity
                         Data.List.NonEmpty Data.Monoid Data.Semigroup Data.Type.Equality
                         Data.Version Data.Void GHC.Exts GHC.Generics GHC.IO.Exception
                         GHC.TypeLits Data.IntMap.Base Data.IntSet.Base Data.Map.Base
                         Data.Sequence Data.Set.Base Text.PrettyPrint.Annotated.HughesPJ
                         Text.PrettyPrint.HughesPJ

Do we *really* have this many type family instances in base and Cabal? I was flummoxed, until I realized that the Generic type class defines a type family!

-- | Representable types of kind *.
-- This class is derivable in GHC with the DeriveGeneric flag on.
class Generic a where
  -- | Generic representation type
  type Rep a :: * -> *
  -- | Convert from the datatype to its representation
  from  :: a -> (Rep a) x
  -- | Convert from the representation to the datatype
  to    :: (Rep a) x -> a

The upshot is that if you derive Generic, you have agreed to a perpetual interface file size tax on every module which transitively depends on your module, as well as lots of fruitless pairwise consistency checking. Ick, especially considering that it's fairly common practice to slap a Generic on every data type you define.

This is a case where we would gain a lot if we could put a local restriction on Generic instances so that individual instances are guaranteed not to overlap, e.g., like one of the rules that Rust uses (http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/) Then we'd avoid balling up a big transitive closure of all modules that wrote deriving Generic. Since non-overlapness is guaranteed by construction, we'd no longer need an eager check.

Related #5224

Trac metadata
Trac field Value
Version 8.1
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC goldfire, simonpj
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information