Is there a Haskell library providing (or assisting in writing) generic isomorphism between a sum type and an associated heterogenous sum type?
Take the following sum type,
data TR = TR_Index | TR_Inner R
Now we associate it with a heterogenous sum NS I xs (where the 'xs' is defined by a type-class; see MotleyRouteSubRoutes below). And then define a isomorphism to convert back and forth:
instance MotleyRoute TR where
-- `SingletonRoute s` is isomorphic to `()`
-- `PrefixedRoute s r` is isomorphic to `Const r s`
type MotleyRouteSubRoutes TR =
'[ SingletonRoute "index.html"
, PrefixedRoute "inner" R
]
motleyRouteIso =
iso
( \case
TR_Index -> Z $ I SingletonRoute
TR_Inner r -> S $ Z $ I $ PrefixedRoute r
)
( \case
Z (I SingletonRoute) -> TR_Index
S (Z (I (PrefixedRoute r))) -> TR_Inner r
S (S x) -> case x of {}
)
My goal here is to write motleyRouteIso generically (as well as define MotleyRouteSubRoutes generically, but that maybe outside the scope here). I was going to do it from scratch using generics-sop, but I wonder if there already is a library that does this for me. generic-optics provides a IsList but that works for products not sums.
(For full context, see this PR)
Having seen no answer on this so far I'll assume for the moment no such prebuilt solution exists. So here's an approach I went with based on
generics-sop.RGenericFirst I added a
RGenerictype-class to narrow the case where a route ADT can have constructors with 0 or 1 product only:The implementation of this instance is fairly straightforward using
generics-sop. You can see it here (theRouteNPconstraint captures the expected "shape" of route types we are interested in).Introduce
Coerciblefor heavy-liftingNote that the individual types in
MotleyRouteSubRoutesare more or less coercible to the route type constructors. So I used that knowledge to "eliminate" the specific types from the equation thus getting one large step closer to a generic implementation. Specifically:Notice how in the new version there is no reference to
SingletonRouteorPrefixedRoute.Full diff here.
Generic Iso
With this place, it is now pretty straightforward to generically implement
motleyRouteIsousingtrans_NSbecause theCoercibleconstraint does all the heavy lifting for us, withRGenericabstracting out dealing with expected 'shape' of the route type. It looks like this:The change for this is here.