How to extract an identifier (in this case an integer) from a structure using GHC.Generics
?
I have an Id
type:
newtype Id = Id { _id :: Int }
and a lot of types that use that type:
data VarId = VarId Name Id SortId
data FuncId = FuncId Name Id [SortId] SortId
data SortId = Name Id
-- ... and 20 more of these things
On top of that there are other types that wrap the types above, for instance, identifiers:
data Identifier = IdVar VarId
| IdFunc FuncId
| IdSort SortId
-- ... and 20 more of these things
Now I need to extract the _id
field from any of these types containing an Id
value. Currently we have tons of boilerplate for this, and I want to scrap it away using generics.
At first I thought about defining a class:
class Identifiable e where
getId :: e -> Id
default getId :: (Generic e, GIdentifiable (Rep e)) => e -> Id
getId = gGetId . from
class GIdentifiable f where
gGetId :: f e -> Id
in such a way that you'd have an Identifiable
instance only if there is one such Id
type inside (in case that there are multiple of those like in FuncId
above we return the first Id
found when traversing the structure from top to bottom). Now the problem comes when I try to define the GIdentifiable
instances for the product and sum. I would like to express something like:
instance (GIdentifiable a) => GIdentifiable (a :*: b) where
gGetId (a :*: _) = gGetId a
instance {-# OVERLAPS #-} (GIdentifiable b) => GIdentifiable (a :*: b) where
gGetId (_ :*: b) = gGetId b
Which won't work because I'm defining duplicate instances.
I could redefine Identifiable
so that getId :: e -> Maybe Id
but this will remove some of the type safety, and introduce unnecessary checks when I know that a type contains at least one Id
.
Is there a way to express this sort of case analysis when working with the type system?