Proposal: Add Eq1 and Ord1 instances in GHC.Generics, and synchronize with Data.Functor.* types

Ryan Scott ryan.gl.scott at gmail.com
Sun Feb 25 23:26:30 UTC 2018


> - GHC.Generics is missing Eq1, Ord1, Show1, Read1 instances

I'd be in favor of this idea. However, see my caveat below.

> Data.Functor.* is missing Semigroup and Monoid instances (that my
> previous proposal would add to GHC.Generics)

This is (or was) by design. See [1] for the full story.

We migrated the Data.Functor.* module hierarchy from transformers,
which is a library that is very much committed to using a Haskell98
style of code. The style of Semigroup/Monoid instances you're
proposing to be added would not be Haskell98, since an instance like
this:

    instance Monoid (f (g p)) => Monoid (Compose f g p)

Would not be Haskell98 due to its use of FlexibleContexts. Of course,
there are other ways one could conceivably define Monoid instances for
these classes that are Haskell98, but as Edward noted in [1], they
each had subtle flaws. So, we punted on the idea of adding
Semigroup/Monoid instances at the time.

Now granted, we didn't adhere to this Haskell98 philosophy everywhere.
We did add Data instances for Compose et al., for example, and those
require FlexibleContexts due to the way they work. But Data is a
GHC-specific class, so this exception is somewhat understandable.

However, you're proposing we make an exception for Semigroup/Monoid.
Is this OK? Granted, the Haskell Report mentions neither to my
knowledge, but at the same time, nothing about them requires
FlexibleContexts on the surface. So I'm a bit ambivalent about the
idea.

Now, back to Eq1, Ord1, Show1, and Read1. The arrival of these classes
into base predates GHC.Generics, so when Eq, Ord, Read, and Show
instances were added for the data types in GHC.Generics, they were of
the form:

    instance (Eq (f (g p)) => Eq ((f :.: g) p)
    instance (Ord (f (g p)) => Ord ((f :.: g) p)
    ...

Of course, this is not what is done for the data types in the
Data.Functor.* hierarchy. For instance, there we have:

    instance (Eq1 f, Eq1 g) => Eq (Compose f g p)
    instance (Ord1 f, Ord1 g) => Ord (Compose f g p)
    ...

So while we could add Eq1, Ord1, Read1, and Show1 instances for the
data types in GHC.Generics, it does serve as a reminder that the
corresponding Eq, Ord, Read, and Show instances are inconsistent with
the ones from the Data.Functor.* space.

Personally, I don't think this need be a dealbreaker—after all, Eq1 et
al. simply weren't around at the time. It just means that if we ever
have a (theoretical) merger of the data types between Data.Functor.*
and GHC.Generics, we'd have to pick one style of Eq/Ord/Read/Show
instances over the other. But this is a bridge we need not cross now.

*tl;dr* I'm +1 in favor of your first idea, and ambivalent about your
second idea.

Ryan S.
-----
[1] https://ghc.haskell.org/trac/ghc/ticket/11135#comment:25


More information about the Libraries mailing list