Consider the following type (in Haskell):
data CType = CInt | CDouble | CProduct String [(String, CType)]
I would like to have a function that would generate corresponding Haskell type declarations. Constructors CInt and CDouble should correspond to Int and Double types thus result in no declaration. (CProduct t ts) should correspond to the record type with name and constructor defined by t and fields defined by ts list. For example:
ty :: CType
ty = CProduct "Type1" [("proj10", CDouble), ("proj11", CProduct "Type0" [("proj0", CInt)])]
should result in two definitions:
data Type0 = Type0 {proj0 :: Int}
data Type1 = Type1 {proj10 :: Double, proj11 :: Type0}
To define this function I use Template Haskell:
genType :: CType -> Q Type
genType CInt = retu (ConT ''Int)
genType CDouble = retu (ConT ''Double)
genType (CProduct s t) =
do
l <- lookupTypeName s
case l of
Just sn -> retu (ConT sn)
Nothing ->
do
sn <- newName s
retu (ConT sn)
genField :: (String, CType) -> Q VarStrictType
genField (s , t) =
do
sn <- newName s
tn <- genType t
retu (sn, NotStrict, tn)
genDecl :: CType -> Q [Dec]
genDecl CInt = retu []
genDecl CDouble = retu []
genDecl (CProduct s t) =
do
ts <- fmap join . sequence . map (genDecl . snd) $ t
res <- lookupTypeName s
case res of
Just _ -> retu ts
Nothing ->
do
sn <- newName s
sc <- newName s
fn <- sequence . map genField $ t
let
dt = DataD [] sn [] [RecC sc fn] []
retu (dt : ts)
When I invoke the function with $(genDecl ty) for ty :: CType defined above I get following error:
The exact Name ‘Type0_adN6’ is not in scope …
Probable cause: you used a unique Template Haskell name (NameU),
perhaps via newName, but did not bind it
But when I generate definitions one by one everything is fine:
$(fmap ([x, y] -> [y]) . genDecl $ ty)
$(genDecl $ ty)
So the question is: how to properly add type declarations in Q monad to generate them all at once?
