如何检查字段类型为类型级计算结果的Haskell记录?
从Servant的Server类型中提取记录字段的最终类型
你可以通过类型级编程结合GHC泛型实现需求——直接从Server API类型中提取所有记录字段的具体类型,无需逐个指定字段。以下是两种可行方案:
方案1:针对特定记录类型的直接实现
如果仅需处理Counter这类特定记录类型,可直接为其定义类型家族,明确提取字段类型:
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} import Servant -- 定义类型类,关联记录的字段类型列表 class RecordFieldTypes a where type FieldTypes a :: [*] -- 为Counter的实例实现类型类 instance RecordFieldTypes (Counter m) where type FieldTypes (Counter m) = [ m :- Capture "stuff" Int :> PostNoContent , m :- Get '[JSON] Int ]
使用时直接查询类型即可:
ghci> :kind! FieldTypes (Server API) FieldTypes (Server API) :: [*] = '[AsServerT Handler :- Capture "stuff" Int :> PostNoContent, AsServerT Handler :- Get '[JSON] Int]
方案2:通用泛型实现(支持所有派生Generic的记录)
如果需要处理任意通过NamedRoutes定义的记录类型,可利用GHC泛型在类型层面遍历记录结构,自动提取所有字段类型:
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE DeriveGeneric #-} import GHC.Generics import Servant -- 内部类型类,处理泛型Rep的结构 class GRecordFieldTypes f where type GFieldTypes f :: [*] -- 忽略数据类型的元信息 instance GRecordFieldTypes (M1 D x f) where type GFieldTypes (M1 D x f) = GFieldTypes f -- 忽略构造器的元信息 instance GRecordFieldTypes (M1 C x f) where type GFieldTypes (M1 C x f) = GFieldTypes f -- 提取单个字段的类型 instance GRecordFieldTypes (M1 S x (K1 R a)) where type GFieldTypes (M1 S x (K1 R a)) = '[a] -- 组合多个字段的类型列表 instance (GRecordFieldTypes f, GRecordFieldTypes g) => GRecordFieldTypes (f :*: g) where type GFieldTypes (f :*: g) = GFieldTypes f ++ GFieldTypes g -- 顶层类型类,对接Generic class RecordFieldTypes a where type FieldTypes a :: [*] instance (Generic a, GRecordFieldTypes (Rep a)) => RecordFieldTypes a where type FieldTypes a = GFieldTypes (Rep a)
这个方案无需为每个记录类型单独写实例,只要记录派生了Generic(比如示例中的Counter),就能自动提取字段类型:
ghci> :kind! FieldTypes (Server API) FieldTypes (Server API) :: [*] = '[AsServerT Handler :- Capture "stuff" Int :> PostNoContent, AsServerT Handler :- Get '[JSON] Int]
两种方案都能在类型层面完成计算,你可以根据是否需要通用性选择合适的实现。
内容的提问来源于stack exchange,提问作者danidiaz
相关产品推荐
相关产品推荐

