Haskell 使用类型族和泛型查找Id值
这个问题涉及到,我想避免从数据结构中提取Haskell 使用类型族和泛型查找Id值,haskell,type-families,ghc-generics,Haskell,Type Families,Ghc Generics,这个问题涉及到,我想避免从数据结构中提取Id值的样板文件,但要以类型安全的方式 我将在这里重复问题的相关细节:假设您有一个类型Id: newtype Id = Id { _id :: Int } 您需要定义一个函数getId,该函数从至少包含一个Id值的任何结构中提取此Id: class Identifiable e where getId :: e -> Id 现在的问题是如何以类型安全的方式定义这样的类,同时使用泛型避免样板文件 在我的演讲中,我被指的是类型家族,尤其是思想
Id
值的样板文件,但要以类型安全的方式
我将在这里重复问题的相关细节:假设您有一个类型Id
:
newtype Id = Id { _id :: Int }
您需要定义一个函数getId
,该函数从至少包含一个Id
值的任何结构中提取此Id
:
class Identifiable e where
getId :: e -> Id
现在的问题是如何以类型安全的方式定义这样的类,同时使用泛型避免样板文件
在我的演讲中,我被指的是类型家族,尤其是思想。据我所知,这个想法是定义一个类型类mkidentification
,以便:
class MakeIdentifiable (res :: Res) e where
mkGetId :: Proxy res -> e -> Id
其中,仅当至少有一个Id
值嵌套在值的内部时,该值的类型为Res
:
data Crumbs = Here | L Crumbs | R Crumbs
data Res = Found Crumbs | NotFound
然后,人们似乎可以定义:
instance MakeIdentifiable (Found e) e => Identifiable e where
getId = mkGetId (Proxy :: Proxy (Found e))
现在的问题是如何为Res
定义与GHC.泛型类型相关联的类型族(U1
,K1
,:*:
,:+:
)
我尝试了以下方法:
type family HasId e :: Res where
HasId Id = Found Here
HasId ((l :+: r) p) = Choose (HasId (l p)) (HasId (r p))
其中,选择
类似于上述博文中的定义:
type family Choose e f :: Res where
Choose (Found a) b = Found (L1 a)
Choose a (Found b) = Found (R1 b)
Choose a b = NotFound
但这不会编译为HasId(lp)具有kind
Res
,而是需要一个类型 您很快就可以进行选择打字检查了L1
和R1
是(:+:)
的构造函数,而不是Crumbs
。还有一个类型GHC.Generics.R::*
,它在类型级别对Crumbs
隐藏R
构造函数,但是您可以使用'R
消除歧义(单引号的名称是构造函数,双引号的名称是类型构造函数)
注释各种类型也是一种很好的做法,就像我们注释顶级函数的类型一样
type family Choose (e :: Res) (f :: Res) :: Res where
Choose (Found a) b = Found ('L a)
Choose a (Found b) = Found ('R b)
Choose NotFound NotFound = NotFound -- I'm not a fan of overlapping families
什么是Choose
?我添加了Choose
可能是什么的描述。