To allow deriving poly-kinded Generic1 instances

Ryan Scott ryan.gl.scott at gmail.com
Wed Oct 13 13:48:54 UTC 2021


> I figured out that this compiles:
>
> data HKD (f :: Type -> Type) = Foo (F1 Int f) (F1 Double f)
>   | Bar (F1 Bool f)
>   deriving Generic1
>
> newtype F1 a f = F1 { unF1 :: f a }

Yes, that's a useful trick to keep in mind. For what it's worth, I think
your `F1` is the same thing as `Barbie` [1] from the `barbies` library.

> Would it be a good idea to add F1 to GHC.Generics?

There's a couple of issues that make me cautious about this idea:

1. This isn't an issue that's specific to `DeriveGeneric`. Other `stock`
deriving strategies that deal with similar classes, such as
`DeriveFunctor`, also suffer from this problem. For instance, you can't do
the following:

   > data T a = MkT (Either a Int) deriving Functor

   Again, the issue is that the last type parameter (`a`) appears in a
field type in a position other than as the last argument. To make _this_
work, you'd need something like `Flip` [2] from the `bifunctors` library:

   > data T a = MkT (Flip Either Int a) deriving Functor

   That leads into the second issue...
2. There are an infinite number of different type variable combinations you
could conceivably add special support for. I've already mentioned `Barbie`
and `Flip` above, but you could just as well put the last type parameter in
other places as well:

   > data S1 a = MkS1 (a, Int, Int) deriving Generic1
   > data S2 a = MkS2 (a, Int, Int, Int) deriving Generic1
   > data S3 a = MkS3 (a, Int, Int, Int, Int) deriving Generic1
   > ...

   And this is only if you assume that the last type parameter only appears
once in each field type. You'd need even more special cases if the last
type parameter appears in multiple places in a field type:

   > data U1 a = MkU1 (a, a) deriving Generic1
   > data U2 a = MkU2 (a, a, a) deriving Generic1
   > ...

   With all of these possibilities, it's difficult to say how far we should
go with this.

Generally speaking, my recommendation for people who are dissatisfied with
`Generic1`'s restrictions on where the last type parameter can be placed is
to not use `Generic1` at all. There are other generic programming libraries
that do not have the same restrictions, such as `kind-generics` [3]. Using
something like `kind-generics` avoids the need to use things like `Barbie`,
`Flip`, etc. in the first place.

Best,

Ryan
-----
[1]
https://hackage.haskell.org/package/barbies-2.0.3.0/docs/Barbies.html#t:Barbie
[2]
https://hackage.haskell.org/package/bifunctors-5.5.11/docs/Data-Bifunctor-Flip.html#t:Flip
[3] https://hackage.haskell.org/package/kind-generics

On Wed, Oct 13, 2021 at 9:26 AM Fumiaki Kinoshita <fumiexcel at gmail.com>
wrote:

> Oh, I drew a conclusion too early when fiddling with a hypothetical
> Generic1 instance. I now think it's not possible to define an instance with
> the current kit.
>
> I figured out that this compiles:
>
> data HKD (f :: Type -> Type) = Foo (F1 Int f) (F1 Double f)
>   | Bar (F1 Bool f)
>   deriving Generic1
>
> newtype F1 a f = F1 { unF1 :: f a }
>
> Problem solved, thanks!
>
> Would it be a good idea to add F1 to GHC.Generics? Omitting metadata, it'd
> derive something like
>
> instance Generic1 HKD where
>   type Rep1 HKD = F1 Int :*: F1 Double :+: F1 Bool
>   from1 (Foo a b) = L1 (F1 a :*: F1 b)
>   from1 (Bar c) = R1 (F1 c)
>   to1 (L1 (F1 a :*: F1 b)) = Foo a b
>   to1 (R1 (F1 c)) = Bar c
>
> I suppose it doesn't affect existing Generic1 instances and uses, so I
> don't expect breakages by adding this
>
> ...
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20211013/c229bf90/attachment.html>


More information about the ghc-devs mailing list