如何获取HasField类型类的全部实例(含GHC自动生成实例)
获取Haskell记录类型的所有HasField实例(含自动生成)
1. 在GHCi中查看自动生成的HasField实例
GHC为记录字段自动生成的HasField实例属于派生隐含实例,默认不会出现在:instances命令的输出里。需要开启两个编译选项:
- 在GHCi中执行:
:set -fprint-explicit-kinds -fprint-deriving-instances
之后再查询具体实例,比如:
:instances HasField "f1" R
就能看到GHC自动生成的HasField "f1" R Int实例,以及手动定义的实例。
2. 用Template Haskell获取所有HasField实例
默认的reify ''HasField只能收集到显式手动定义的实例,要获取自动生成的实例,需要调整编译选项并使用reifyInstances函数:
必要的编译选项
- 开启扩展:
TemplateHaskell、DataKinds、GHC.Records(引入HasField类型类) - 编译时添加
-fprint-deriving-instances,让GHC把派生的实例暴露给Template Haskell
示例代码
{-# LANGUAGE TemplateHaskell, DataKinds, GHC.Records #-} import Language.Haskell.TH import GHC.Records (HasField(..)) -- 定义记录类型,GHC自动为f1、f2生成HasField实例 data R = R { f1 :: Int, f2 :: String } deriving (Show) -- 手动定义的HasField实例 instance HasField "f3" R Bool where getField _ = True -- 获取指定字段的HasField实例(含自动生成) getHasFieldInstance :: String -> Q [InstanceDec] getHasFieldInstance fieldName = reifyInstances ''HasField [ ConT ''R , LitT (StrTyLit fieldName) -- 用WildCardT匹配任意字段类型,部分GHC版本支持 , WildCardT ] -- 批量获取R类型的所有HasField实例(自动+手动) getAllRHasFieldInstances :: Q [InstanceDec] getAllRHasFieldInstances = do -- 先获取R的类型信息,提取所有字段名 tyConInfo <- reify ''R let fieldNames = case tyConInfo of TyConI (DataD _ _ _ _ [RecC _ fields] _) -> map (\(fieldName, _, _) -> nameBase fieldName) fields _ -> [] -- 加上手动定义的字段名(比如"f3") let allFields = fieldNames ++ ["f3"] -- 批量查询每个字段的实例 concat <$> mapM getHasFieldInstance allFields
注意事项
- 如果在GHCi中加载该模块,必须先执行
:set -fprint-deriving-instances再:load,否则Template Haskell无法获取自动生成的实例。 - 若你的GHC版本不支持
WildCardT,需要显式传入对应字段的类型构造器(比如ConT ''Int对应f1)。
内容的提问来源于stack exchange,提问作者lsmor
相关产品推荐
相关产品推荐

