well-typed / generics-sop

Generic Programming using True Sums of Products
BSD 3-Clause "New" or "Revised" License
157 stars 48 forks source link

1. Implement `SOP.Generic (GHC.Generically a)` and 2. export SOP.Generically #153

Open Icelandjack opened 2 years ago

Icelandjack commented 2 years ago

Here are 2 suggestions once we get GHC.Generically and GHC.Generically1 in base 4.17.

Those are simply newtypes, if you have a definition in terms of GHC.Generic or GHC.Generic1 then you give it an instance.

-- GHC.Generics
newtype Generically    a = Generically  a
newtype Generically1 f a = Generically1 (f a)
  1. SOP.Generic has an implementation in terms of GHC.Generic, thus I propose the following instance:

    instance (GHC.Generic a, GFrom a, GTo a, Rep a ~ SOP I (GCode a))
          => SOP.Generic (GHC.Generically a) where
      type Code (GHC.Generically a) = GCode a
      from = gfrom
      to   = gto

    This way an instance of SOP.Generic T can be derived: deriving SOP.Generic via GHC.Generically T.

  2. I also propose defining a newtype with the same name as Generically, to be imported qualified with SOP.Generically, it will serve the same purpose but for SOP.Generic definitions. I think every data-type generic library should export a name like that. If I can implement Binary in terms of SOP.Generic I define instance (SOP.Generic a, ..) => Binary (SOP.Generically a):

    -- Generics.SOP
    newtype Generically a = Generically a

A newtype like SOP.Generically can be combined with a library like generic-override that overrides the generic representation of an instance, so we decouple a generic implementation from modifying the generic implementation:

data X = X { s :: Int, p :: Int }
  deriving stock GHC.Generic

  -- 1.
  deriving SOP.Generic via GHC.Generically X

  -- 2. 
  deriving (Semigroup, Monoid) via SOP.Generically
    (Override X
      '[ "x" `As` Sum Int
       , "y" `As` Product Int
       ])
kosmikus commented 2 years ago

Thanks! In principle, I'm in support of both, although regarding part 2, I'm somewhat afraid that the presence of the type will go mostly unnoticed, because the library itself has hardly any generic functions, so it would basically be a type without instances.

Icelandjack commented 2 years ago

For 1. I think this pragma should work

#if MIN_VERSION_base(4,17,0)
#define HAS_GENERICALLY
#endif

And then this is a backwards compatible definition for GHC.Generically

#ifdef HAS_GENERICALLY
instance .. => SOP.Generic (GHC.Generically a) where
  .. 
#endif

For 2. I share the concern, I was thinking that it would mostly be up to other libraries that depend on generics-sop to specify instances without having to depend on another library. generics-sop can add instances for the base classes with time.

Maybe it belongs in basic-sop instead? It already has a generics-sop Eq instance, but there I feel like it would go unnoticed as well.

Icelandjack commented 1 year ago

For 1. you can use the generically compatibility package.

I think Generically¹ should be defined so people can start attaching instances to it, I would expect that if I have the generics-sop package that I can define generic instances. It just depends if you mind defining orphan instances in basic-sop.

¹ What name should be chosen? SOPGenerically? At first I thought of qualified Generically: SOP.Generically but I don't like the idea of dealing with the clash.

import Generics.SOP hiding (Generically(..))
import qualified Generics.SOP as SOP (Generically)
phadej commented 1 year ago

There is already a name clash in Generic type-class name.

Icelandjack commented 1 year ago

So you're fine with exporting this as Generically?

phadej commented 1 year ago

So you're fine with exporting this as Generically?

It's up to @kosmikus

kosmikus commented 1 year ago

Yes, as long as we don't rename the Generic class, I don't see an issue with calling the newtype Generically as well.

@Icelandjack do you want to submit a PR? Otherwise, I'll try to do this whenever I next get around to making changes on the library.

Icelandjack commented 1 year ago

I made a PR #159 and updated some of the documentation. I didn't build the project so there may be some errors.