10

最近の交換に基づいて、コンパイル時の型の安全性を確保するために、テンプレートHaskellを使用してコードを生成することを確信しました。

レコードフィールドの名前とタイプを調べる必要があります。を使用してフィールド名を取得できることを理解していconstrFields . toConstr :: Data a => a -> [String]ます。しかし、私はフィールド名以上のものが必要です、私はそれらのタイプを知る必要があります。たとえば、タイプがのフィールドの名前を知る必要がありますBool

レコード、フィールド名、フィールドタイプf :: a -> [(String, xx)]である関数を作成するにはどうすればよいですか?aStringxx

4

1 に答える 1

12

Infoタイプは、によって提供される値で、他のすべてと一緒に使用できる必要がありますreify。具体的にはvalueTyConIを含むを取得する必要があります。そこから、コンストラクターを指定する値のリストを取得できます。次に、レコード タイプは を使用する必要があります。これにより、フィールド名、フィールドが厳密かどうか、タイプを含むタプルによって記述されるフィールドのリストが得られます。DecConRecC

そこからどこへ行くかは、これらすべてで何をしたいかによって異なります。


編集:上記を実際に実証するために、レコードフィールドを見つける本当にひどいクイックでダーティな関数を次に示します。

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 に答える