[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