10

根据最近的一次交流,我被说服使用 Template Haskell 生成一些代码以确保编译时类型安全。

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

如何构造一个函数,记录在哪里,f :: a -> [(String, xx)]字段名是字段类型?aStringxx

4

1 回答 1

12

该类型应与其他所有内容一起在Info提供的值中可用reify。具体来说,您应该得到一个TyConI,其中包含一个Decvalue,您可以从中获取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")])].

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

于 2011-12-30T23:37:52.220 回答