如何使用从结构中提取标识符(在本例中为整数)GHC.Generics
?
我有一个Id
类型:
newtype Id = Id { _id :: Int }
以及许多使用该类型的类型:
data VarId = VarId Name Id SortId
data FuncId = FuncId Name Id [SortId] SortId
data SortId = Name Id
-- ... and 20 more of these things
除此之外,还有其他类型可以包装上述类型,例如标识符:
data Identifier = IdVar VarId
| IdFunc FuncId
| IdSort SortId
-- ... and 20 more of these things
现在我需要_id
从这些包含Id
值的任何类型中提取字段。目前我们有大量的样板,我想使用泛型将其废弃。
一开始我想定义一个类:
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
这样,只有当内部有一个这样的类型时,你才会有一个Identifiable
实例(如果上面有多个这样的类型,我们返回从上到下遍历结构时找到的第一个)。现在,当我尝试为产品和总和定义实例时,问题就来了。我想表达类似的东西:Id
FuncId
Id
GIdentifiable
instance (GIdentifiable a) => GIdentifiable (a :*: b) where
gGetId (a :*: _) = gGetId a
instance {-# OVERLAPS #-} (GIdentifiable b) => GIdentifiable (a :*: b) where
gGetId (_ :*: b) = gGetId b
这不起作用,因为我正在定义重复的实例。
我可以重新定义Identifiable
,getId :: e -> Maybe Id
但这会删除一些类型安全,并在我知道一个类型至少包含一个时引入不必要的检查Id
。
在使用类型系统时,有没有办法表达这种案例分析?