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.sc...@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 <fumiex...@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@haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs >
_______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs