
#15969: Generic1 deriving should use more coercions -------------------------------------+------------------------------------- Reporter: dfeuer | Owner: (none) Type: bug | Status: new Priority: normal | Milestone: 8.8.1 Component: Compiler | Version: 8.6.2 Resolution: | Keywords: Generics, | Deriving Operating System: Unknown/Multiple | Architecture: Type of failure: Runtime | Unknown/Multiple performance bug | Test Case: Blocked By: | Blocking: Related Tickets: | Differential Rev(s): Wiki Page: | -------------------------------------+------------------------------------- Comment (by RyanGlScott): This is a very interesting observation. I, too, have been rather annoyed by the fact that `Generic1` requires using `fmap` to implement instances involving `(:.:)`. In fact, I've pondered using `Coercible`-plus-`QuantifiedConstraints` to get replace these `fmap`s in [https://gist.github.com/RyanGlScott/cca1a0605a3b460c4af073cfce3c15fb this gist]. (It comes with its [https://ghc.haskell.org/trac/ghc/ticket/8516#comment:7 own set of problems], which is why I haven't pursued the idea further.) Remarkably, however, this technique bypasses the need to use `fmap` entirely! I'm not sure why `Generic1` didn't pick this convention to start with—perhaps they wanted `(:.:)` to be right-associative as a sort of parallel the function composition operator `(.)`, which is also associative? I can't say. Unfortunately, `(:.:)` being right-associative //is// a well established convention at this point, and switching it to be left-associative would break lots of code in the wild. Most instances of `(:.:)` tend to look like this: {{{#!hs instance (Cls f, GCls g) => GCls (f :.: g) where gmeth = ... class Cls f where meth :: ... default meth :: (Generic1, GCls (Rep1 f)) => ... meth = ... gmeth ... class GCls f where gmeth :: ... }}} That is, you give the outer type `f` an instance of the "base" class (`Cls`) and the inner type `g` an instance of the "generified" class (`GCls`). If we made `(:.:)` left-associative, then we'd have to turn this convention around and instead define: {{{#!hs instance (GCls f, Cls g) => GCls (f :.: g) where gmeth = ... }}} Is the breakage worth it? I'm inclined to say "no", since if we're going to make a backwards-incompatible change to `GHC.Generics`, then our effort might be better spent incorporating a more modern generic programming library into GHC (call it `GHC.Generics2`, perhaps) and slowly deprecating `GHC.Generics` in favor of that one. And thankfully, most modern generic programming frameworks no longer use `(:.:)`, so it's simply a non-issue there. -- Ticket URL: http://ghc.haskell.org/trac/ghc/ticket/15969#comment:4 GHC http://www.haskell.org/ghc/ The Glasgow Haskell Compiler