[GHC] #10598: DeriveAnyClass and GND don't work well together

GHC ghc-devs at haskell.org
Thu Aug 4 13:46:12 UTC 2016


#10598: DeriveAnyClass and GND don't work well together
-------------------------------------+-------------------------------------
        Reporter:  osa1              |                Owner:  RyanGlScott
            Type:  bug               |               Status:  patch
        Priority:  normal            |            Milestone:  8.2.1
       Component:  Compiler          |              Version:  7.11
      Resolution:                    |             Keywords:  Generics
Operating System:  Unknown/Multiple  |         Architecture:
                                     |  Unknown/Multiple
 Type of failure:  None/Unknown      |            Test Case:
      Blocked By:                    |             Blocking:
 Related Tickets:                    |  Differential Rev(s):  Phab:D2280
       Wiki Page:                    |
-------------------------------------+-------------------------------------

Comment (by RyanGlScott):

 oerjan, hopefully this answers all of your questions:

 Replying to [comment:42 oerjan]:
 > * Might it be simpler to treat the bespoke -> newtype optimization as a
 post-step, independent of the rest? Or is it ever important ''not'' to
 apply it? Even for step 1.

 Yes, there are cases where you don't want to replace the `bespoke`
 strategy with the `newtype` one. Consider the following example:

 {{{#!hs
 newtype Foo a = Foo (Maybe a)
 }}}

 What would happen if you tried implementing a `Traversable` instance for
 `Foo`? As it turns out, trying to define `traverse = coerce` is impossible
 at the moment:

 {{{
     • Couldn't match representation of type ‘f (Maybe b)’
                                with that of ‘f (Foo b)’
         arising from the coercion of the method ‘traverse’
           from type ‘forall (f :: * -> *) a b.
                      Applicative f =>
                      (a -> f b) -> Maybe a -> f (Maybe b)’
             to type ‘forall (f :: * -> *) a b.
                      Applicative f =>
                      (a -> f b) -> Foo a -> f (Foo b)’
       NB: We cannot know what roles the parameters to ‘f’ have;
         we must assume that the role is nominal
     • When deriving the instance for (Traversable Foo)
 }}}

 This is because the type signature of `traverse` leads to an ill-roled
 coercion (see
 [https://ghc.haskell.org/trac/ghc/wiki/Roles#RolesandTraversable here] for
 the full story). Until #9123 is fixed, GHC will refuse to apply the
 `newtype` strategy to derived `Traversable` instances (unless you
 explicitly ask for it).

 BTW, the full list of classes that GHC avoids using the `newtype` strategy
 for can be found
 [http://git.haskell.org/ghc.git/blob/8ecac2512aed557b4f59fd697eabd3ef9ddfd6e9:/compiler/typecheck/TcDeriv.hs#l1562
 here].

 > * Is 2(a) missing `Enum`?

 No. The full list of "standard" classes for which `newtype` kicks in by
 default (without the presence of `-XGeneralizedNewtypeDeriving`) can be
 found
 [http://git.haskell.org/ghc.git/blob/8ecac2512aed557b4f59fd697eabd3ef9ddfd6e9:/compiler/typecheck/TcDeriv.hs#l1552
 here]. As the comments there indicate, `Enum` isn't in that list because
 by default, deriving `Enum` for a newtype would fail since it checks for a
 datatype with all nullary constructors. Therefore, you have to enable
 `-XGeneralizedNewtypeDeriving` (or use the `newtype` keyword) to derive
 `Enum` for a newtype.

 > * Why is `Traversable` in 2(b)? I would have thought that the bespoke ->
 newtype optimization would apply to it. I guess there's some technical
 difference.

 See my answer above.

 > * All the examples in 2(c) are listed in a or b, leaving the strange
 impression that it can never be triggered. Although I assume `Functor` and
 `Foldable` belong there. (Which also tells me the last paragraph in my
 previous comment has been taken care of).

 Sorry, I should have explicitly enumerated the remaining classes not
 listed in 2(a) or 2(b). They are `Functor`, `Foldable`, and `Enum`.

 > * The phrase "can be successfully used with GeneralizedNewtypeDeriving"
 is needed in 2(c) as well. Should 2(d) apply or not if that check falls?
 If it does then sometimes standard classes ''could'' get anyclassed. If it
 does ''not'', then should the classes fall back to bespoke, at least if
 their extensions are enabled?

 Yes, I should use that phrase, thank you for noticing that omission. I
 certainly don't want standard classes to be derived with the `anyclass`
 strategy.

 > * It seems that `Functor` and `Foldable` never get newtype derived if
 that extension is not enabled, even when it would be safe to do so.

 You have to explicitly enable `-XGeneralizedNewtypeDeriving` to derive
 `Functor` or `Foldable` that way because, by default, deriving them is
 guarded behind `-XDeriveFunctor` and `-XDeriveFoldable`. You can't just
 say `newtype Foo a = Foo (Maybe a) deriving Functor` and expect it to work
 without extensions, unlike `Eq`, `Ord`, `Ix`, and `Bounded`.

 > I think the algorithm can be simplified a bit: remove point 2(c)
 entirely, and say explicitly in point 2(d) that it ''doesn't'' apply to
 standard derivable classes.

 I disagree. 2(a), (b), and (c) are all special cases for standard
 derivable classes, each with enough nuances that trying to cram one of
 them into 2(d) would make it even more confusing (than it already is).

 > Lastly, while thinking about this, I made a table of the (currently
 four-way) classification of the "standard/bespoke" classes:

 Thank you, this is fantastic! I've updated the wiki page with the
 corrections above and incorporating your table (with some slight
 corrections).

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/10598#comment:43>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list