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

GHC ghc-devs at haskell.org
Fri Aug 5 04:01:06 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 oerjan):

 Replying to [comment:43 RyanGlScott]:
 > oerjan, hopefully this answers all of your questions:

 I'm afraid not quite, also now I have some new ones :)

 > 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:

 Although that answered my later question about `Traversable`, what I meant
 here was whether to treat it as a common post-step for the classes that
 ''can'' be newtype derived. In particular `Functor` and `Foldable`, see
 below.

 > 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:

 Ah, similar to that "`Monad` not getting `join` as a method" situation.

 > Until #9123 is fixed, GHC will refuse to apply the `newtype` strategy to
 derived `Traversable` instances (unless you explicitly ask for it).

 Surely with the role situation, it will always fail even if you ask for
 it? Also, I noticed in the code you linked a comment that it wouldn't be
 equivalent in any case for law-breaking `Applicatives`.

 > `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.

 Ah, I misremembered what types can derive `Enum`.

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

 By what you said otherwise, shouldn't `Enum` be in 2(b)?

 > > * 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.

 My point in the rest of that comment was that even adding that phrase
 isn't ''enough'', because the wording and fallthrough still implies that
 anyclass would be used in this case:

 {{{#!hs
 {-# LANGUAGE GeneralizedNewtypeDeriving, DeriveAnyClass #-}

 newtype F x = F ([x], Maybe x) deriving Functor
 }}}

 In fact, even assuming anyclass is not used, that example is a bit
 worrisome: What exactly does GHC do in this case? Does it succeed in
 deriving `Functor`? (I assume No.) If it fails, does it give an error
 message that doesn't confuse the user about why it fails?

 > > * 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`.

 What I meant here, relevant to my post-step question, is that unlike with
 e.g. `Eq` and `Ord`, the algorithm description implies that the newtype
 deriving optimization is not automatic: it's not applied when deriving
 `Functor` or `Foldable` with `DeriveFunctor`/`DeriveFoldable` but not GND
 enabled.

 > > 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).

 You're welcome, although the table was meant to summarize ''all'' the data
 for what determines automatic strategy selection, not just whether GND can
 be used. (I'm a bit unsure whether it was a good idea to separate the
 first two rows.) In particular, "Requires -XGeneralizedNewtypeDeriving"
 only applies to the middle right cell, not the whole column, since only
 the first two rows even allow GND to be automatically selected.

 I tried rewriting the description from a more class/table-centric
 perspective. (In the process, I seem to have changed "standard derivable
 class" into "class which has a bespoke strategy".)

 1. Look for a deriving strategy. If one is present, use that.

 2. If deriving a class which has a bespoke strategy:

    (a) If deriving `Eq`, `Ord`, `Ix`, or `Bounded` for a newtype, use the
 `GeneralizedNewtypeDeriving` strategy (even if the language extension
 isn't enabled).

    (b) If deriving `Functor`, `Foldable`, or `Enum`(?) for a newtype, the
 datatype can be successfully used with `GeneralizedNewtypeDeriving`, and
 `-XGeneralizedNewtypeDeriving` has been enabled, use the
 `GeneralizedNewtypeDeriving` strategy.

    (c) Otherwise, if deriving a class which has a bespoke strategy, and
 the corresponding language extension is enabled (if necessary), use the
 bespoke strategy. If the language extension is not enabled, throw an
 error.

 3. If deriving a class without a bespoke strategy:

    (a) If deriving an instance for a newtype and both
 `-XGeneralizedNewtypeDeriving` and `-XDeriveAnyClass` are enabled, default
 to `DeriveAnyClass`, but emit a warning stating the ambiguity.

    (b) Otherwise, if `-XDeriveAnyClass` is enabled, use `DeriveAnyClass`.

    (c) Otherwise, if deriving an instance for a newtype, the datatype and
 typeclass can be successfully used with `GeneralizedNewtypeDeriving`, and
 `-XGeneralizedNewtypeDeriving` is enabled, do so.

    (d) Otherwise, throw an error.

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


More information about the ghc-tickets mailing list