根据最近的一次交流,我被说服使用 Template Haskell 生成一些代码以确保编译时类型安全。
我需要反省记录字段名称和类型。我知道我可以使用constrFields . toConstr :: Data a => a -> [String]
. 但我需要的不仅仅是字段名称,我还需要知道它们的类型。例如,我需要知道类型为 的字段的名称Bool
。
如何构造一个函数,记录在哪里,f :: a -> [(String, xx)]
字段名是字段类型?a
String
xx
该类型应与其他所有内容一起在Info
提供的值中可用reify
。具体来说,您应该得到一个TyConI
,其中包含一个Dec
value,您可以从中获取Con
指定构造函数的值列表。然后应该使用记录类型RecC
,它将为您提供由包含字段名称、字段是否严格以及类型的元组描述的字段列表。
你从那里去哪里取决于你想用这一切做什么。
编辑:为了实际演示上述内容,这里有一个非常糟糕的快速而肮脏的功能,可以找到记录字段:
import Language.Haskell.TH
test :: Name -> Q Exp
test n = do rfs <- fmap getRecordFields $ reify n
litE . stringL $ show rfs
getRecordFields :: Info -> [(String, [(String, String)])]
getRecordFields (TyConI (DataD _ _ _ cons _)) = concatMap getRF' cons
getRecordFields _ = []
getRF' :: Con -> [(String, [(String, String)])]
getRF' (RecC name fields) = [(nameBase name, map getFieldInfo fields)]
getRF' _ = []
getFieldInfo :: (Name, Strict, Type) -> (String, String)
getFieldInfo (name, _, ty) = (nameBase name, show ty)
在另一个模块中导入它,我们可以像这样使用它:
data Foo = Foo { foo1 :: Int, foo2 :: Bool }
foo = $(test ''Foo)
将其加载到 GHCi 中,其中的foo
值为[("Foo",[("foo1","ConT GHC.Types.Int"),("foo2","ConT GHC.Types.Bool")])]
.
这会给你一个粗略的想法吗?