Haskell反省了记录的字段名称和类型

时间:2011-12-30 23:19:29

标签: haskell template-haskell

基于recent exchange,我一直坚信使用Template Haskell生成一些代码来确保编译时类型的安全性。

我需要内省记录字段名称和类型。我知道我可以constrFields . toConstr :: Data a => a -> [String]使用Bool。但我需要的不仅仅是字段名称,我需要知道它们的类型。例如,我需要知道f :: a -> [(String, xx)]类型字段的名称。

如何构建一个函数a,其中String是记录,xx是字段名称,{{1}}是字段类型?

1 个答案:

答案 0 :(得分:9)

该类型应与Info提供的reify值一起提供,以及其他所有内容。具体来说,您应该获得TyConI,其中包含a Dec value,您可以从中获取Con values specifying the constructors的列表。然后,记录类型应使用RecC,它会为您提供包含字段名称的字段described by a tuple列表,字段是否严格,以及the type

你去哪里取决于你想要做什么。


编辑:为了实际展示上述内容,这里有一个非常糟糕的快速和脏功能,可以找到记录字段:

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")])]

这会给你一个粗略的想法吗?