
#13368: Derive superclasses automatically if possible -------------------------------------+------------------------------------- Reporter: Iceland_jack | Owner: (none) Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: 8.0.1 Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: | Blocking: Related Tickets: #10607 | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): This proposal shares many similarities with the [https://ghc.haskell.org/trac/ghc/wiki/IntrinsicSuperclasses IntrinsicSuperclasses] and [https://ghc.haskell.org/trac/ghc/wiki/InstanceTemplates InstanceTemplates] proposals, so I'll link them here. However, this proposal shares many of their downsides, which I'll briefly expound upon here. First, there's the technical challenges to this proposal. Not all instances are made equal, and `deriving` exposes this in many ways. For instance, suppose you tried this under your proposal: {{{#!hs class Traversable f => Witherable f where ... newtype Wrapped f a = Wrap (f a) deriving newtype Witherable }}} This would attempt the following: * `deriving newtype instance Functor f => Functor (Wrapped f)` * `deriving newtype instance Foldable f => Foldable (Wrapped f)` * `deriving newtype instance Traversable f => Traversable (Wrapped f)` * `deriving newtype instance Witherable f => Witherable (Wrapped f)` But trying to derive `Traversable` that way will fail, as it is ill-roled! See #13153. So not only would this throw an error, but it would throw an error that talks about a completely different class than `Witherable`. Mysterious. What's more, there's quite a number of ambiguities you'd have to deal with. For instance, what happens under your proposal in this scenario? {{{#!hs class Show a => MyShow a where ... data Foo deriving newtype MyShow instance Show Foo }}} Since `Show` is a superclass of `MyShow`, you might expect the `deriving newtype MyShow` clause to derive `instance Show Foo` as well. But there's already an `instance Show Foo`! So we'd either have to error here, or come up with a scheme for defaulting to existing instances when scenarios like the above one crop up. You might respond: "But that's easy! Just use the existing `Show Foo` instance!" But it gets even hairier than that: {{{#!hs class Show a => MyShow1 a where ... class Show a => MyShow2 a where ... data Foo deriving newtype MyShow1 deriving anyclass MyShow2 }}} Now we must ask: which of the two deriving clauses will implicitly derive `Show` behind the scenes? The choice matters, because a `newtype`-derived `Show` instance will be completely different than an `anyclass`-derived one! If the answer to this question is "use the first deriving clause", then now the order in which you write `deriving` clauses matters, which is a property that Haskell has never had up to this point. Finally, there's one more objection I will make not on technical grounds, but on aesthetic ones. This proposal would lose the property that all instances are explicitly asked for by the user. I (personally) find the idea of instances being snuck in behind the scenes quite shady and hard to reason about, and in addition, we'd lose things like concrete source locations for every instance, the ability to annotate all instances with Haddocks, etc. So my opinion in that it's a huge amount of complexity for very little gain and many drawbacks. I like the idea of doing something like this in Template Haskell, so I'm glad that someone is pursuing this in #10607. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/13368#comment:1 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler