[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