To allow deriving poly-kinded Generic1 instances

Edward Kmett ekmett at gmail.com
Thu Oct 14 03:43:35 UTC 2021


I use the same F1 trick (with the same name and for the same reason) in the
HEAD branch of hkd and distributive, but I admit it is a bit frustrating,
because then I have to expose pattern synonyms to hide its boilerplate from
users.

e.g.

https://github.com/ekmett/hkd/blob/85cee5aa594b66f2d03d6366df776ced742e4635/src/Data/HKD.hs#L168

...

https://github.com/ekmett/hkd/blob/85cee5aa594b66f2d03d6366df776ced742e4635/src/Data/HKD.hs#L252

Adding an F1 or the like to GHC.Generics that was used automatically to
handle application of the last "1" argument to some other type in turn
would go a long way towards plugging that hole in the vocabulary of stock
GHC.Generics.

Other generics libraries exist, but they don't get quite the same attention
and user support.

-Edward

On Wed, Oct 13, 2021 at 9:49 AM Ryan Scott <ryan.gl.scott at gmail.com> wrote:

> > 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
>>
>> ...
>>
> _______________________________________________
> ghc-devs mailing list
> ghc-devs at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20211013/472f2952/attachment.html>


More information about the ghc-devs mailing list