module Data.Generics.SYB.WithClass.Derive where
import Language.Haskell.TH
import Data.List
import Data.Char
import Control.Monad
import Data.Maybe
import Data.Generics.SYB.WithClass.Basics
maxTypeParams :: Int
maxTypeParams = 7
deriveTypeablePrim :: Name -> Int -> Q [Dec]
deriveTypeablePrim name nParam
#ifdef __HADDOCK__
= undefined
#else
| nParam <= maxTypeParams =
sequence
[ instanceD (return [])
(conT typeableName `appT` conT name)
[ funD typeOfName [clause [wildP] (normalB
[| mkTyConApp (mkTyCon $(litE $ stringL (nameBase name))) [] |]) []]
]
]
| otherwise = error ("Typeable classes can only have a maximum of " ++
show maxTypeParams ++ " parameters")
where
typeableName
| nParam == 0 = mkName "Typeable"
| otherwise = mkName ("Typeable" ++ show nParam)
typeOfName
| nParam == 0 = mkName "typeOf"
| otherwise = mkName ("typeOf" ++ show nParam)
#endif
deriveDataPrim :: Name -> [Type] -> [(Name, Int)] -> [(Name, [(Maybe Name, Type)])] -> Q [Dec]
deriveDataPrim name typeParams cons terms =
#ifdef __HADDOCK__
undefined
#else
do sequence (
conDecs ++
[ dataTypeDec
, instanceD context (dataCxt myType)
[ funD 'gfoldl
[ clause ([wildP] ++ (map (varP . mkName) ["f", "z", "x"]))
(normalB $ caseE (varE (mkName "x")) (map mkMatch cons))
[]
]
, funD 'gunfold
[clause ([wildP] ++ (map (varP. mkName) ["k", "z", "c"]))
(if (null cons) then (normalB [| error "gunfold : Type has no constructors" |])
else (normalB $ caseE (varE (mkName "constrIndex") `appE` varE (mkName "c")) mkMatches)) []]
, funD 'toConstr
[ clause [wildP, varP (mkName "x")]
(normalB $ caseE (varE (mkName "x"))
(zipWith mkSel cons conVarExps))
[]
]
, funD 'dataTypeOf
[ clause [wildP, wildP] (normalB $ varE (mkName theDataTypeName)) []
]
]
])
where
types = filter (\x -> case x of (VarT _) -> False; _ -> True) $ map snd $ concat $ map snd terms
fieldNames = let fs = map (map fst.snd) terms in
map (\x -> if (null x || all isNothing x) then [] else map (maybe "" show) x) fs
nParam = length typeParams
myType = foldl AppT (ConT name) typeParams
dataCxt typ = conT ''Data `appT` varT (mkName "ctx") `appT` return typ
satCxt typ = conT ''Sat `appT` (varT (mkName "ctx") `appT` return typ)
dataCxtTypes = nub (typeParams ++ types)
satCxtTypes = nub (myType : types)
context = cxt (map dataCxt dataCxtTypes ++ map satCxt satCxtTypes)
mkMatch (c,n) =
do vs <- mapM (\s -> newName s) names
match (conP c $ map varP vs)
(normalB $ foldl
(\e x -> (varE (mkName "f") `appE` e) `appE` varE x)
(varE (mkName "z") `appE` conE c)
vs
) []
where names = take n (zipWith (++) (repeat "x") (map show [0 :: Integer ..]))
mkMatches = map (\(n, (cn, i)) -> match (litP $ integerL n) (normalB $ reapply (appE (varE $ mkName "k")) i (varE (mkName "z") `appE` conE cn)) []) (zip [1..] cons)
where
reapply _ 0 f = f
reapply x n f = x (reapply x (n1) f)
lowCaseName = map toLower nameStr
nameStr = nameBase name
theDataTypeName = lowCaseName ++ "DataType"
dataTypeDec = funD (mkName theDataTypeName)
[clause []
(normalB
[| mkDataType nameStr $(listE (conVarExps)) |]) [] ]
numCons = length cons
constrNames =
take numCons (map (\i -> theDataTypeName ++ show i ++ "Constr") [1 :: Integer ..])
conNames = map (nameBase . fst) cons
conVarExps = map (varE . mkName) constrNames
conDecs = zipWith3 mkConstrDec constrNames conNames fieldNames
where
mkConstrDec decNm conNm fieldNm =
funD (mkName decNm)
[clause []
(normalB
[| mkConstr $(varE (mkName theDataTypeName)) conNm fieldNm
$(fixity conNm)
|]) []]
fixity (':':_) = [| Infix |]
fixity _ = [| Prefix |]
mkSel (c,n) e = match (conP c $ replicate n wildP)
(normalB e) []
#endif
deriveMinimalData :: Name -> Int -> Q [Dec]
deriveMinimalData name nParam = do
#ifdef __HADDOCK__
undefined
#else
decs <- qOfDecs
let listOfDecQ = map return decs
sequence
[ instanceD context
(conT ''Data `appT` (foldl1 appT ([conT name] ++ typeQParams)))
listOfDecQ ]
where
paramNames = take nParam (zipWith (++) (repeat "a") (map show [0 :: Integer ..]))
typeQParams = map (\nm -> varT (mkName nm)) paramNames
context = cxt (map (\typ -> conT ''Data `appT` typ) typeQParams)
qOfDecs =
[d| gunfold _ _ _ = error ("gunfold not defined")
toConstr x = error ("toConstr not defined for " ++
show (typeOf x))
dataTypeOf x = error ("dataTypeOf not implemented for " ++
show (typeOf x))
gfoldl _ z x = z x
|]
#endif
typeInfo :: DecQ -> Q (Name, [Name], [(Name, Int)], [(Name, [(Maybe Name, Type)])])
typeInfo m =
do d <- m
case d of
DataD {} ->
return $ (simpleName $ name d, paramsA d, consA d, termsA d)
NewtypeD {} ->
return $ (simpleName $ name d, paramsA d, consA d, termsA d)
_ -> error ("derive: not a data type declaration: " ++ show d)
where
consA (DataD _ _ _ cs _) = map conA cs
consA (NewtypeD _ _ _ c _) = [ conA c ]
consA d = error ("consA: Unexpected decl: " ++
show d)
paramsA (DataD _ _ ps _ _) = ps
paramsA (NewtypeD _ _ ps _ _) = ps
paramsA d = error ("paramsA: Unexpected decl: " ++
show d)
termsA (DataD _ _ _ cs _) = map termA cs
termsA (NewtypeD _ _ _ c _) = [ termA c ]
termsA d = error ("termsA: Unexpected decl: " ++
show d)
termA (NormalC c xs) = (c, map (\x -> (Nothing, snd x)) xs)
termA (RecC c xs) = (c, map (\(n, _, t) -> (Just $ simpleName n, t)) xs)
termA (InfixC t1 c t2) = (c, [(Nothing, snd t1), (Nothing, snd t2)])
termA (ForallC _ _ c) = termA c
conA (NormalC c xs) = (simpleName c, length xs)
conA (RecC c xs) = (simpleName c, length xs)
conA (InfixC _ c _) = (simpleName c, 2)
conA (ForallC _ _ c) = conA c
name (DataD _ n _ _ _) = n
name (NewtypeD _ n _ _ _) = n
name d = error $ show d
simpleName :: Name -> Name
simpleName nm =
let s = nameBase nm
in case dropWhile (/=':') s of
[] -> mkName s
_:[] -> mkName s
_:t -> mkName t
deriveOne :: Name -> Q [Dec]
deriveOne n =
do info' <- reify n
case info' of
TyConI d -> deriveOneDec d
_ -> error ("derive: can't be used on anything but a type " ++
"constructor of an algebraic data type")
deriveOneDec :: Dec -> Q [Dec]
deriveOneDec dec =
do (name, param, ca, terms) <- typeInfo (return dec)
t <- deriveTypeablePrim name (length param)
d <- deriveDataPrim name (map VarT param) ca terms
return (t ++ d)
deriveOneData :: Name -> Q [Dec]
deriveOneData n =
do info' <- reify n
case info' of
TyConI i -> do
(name, param, ca, terms) <- typeInfo ((return i) :: Q Dec)
d <- deriveDataPrim name (map VarT param) ca terms
return d
_ -> error ("derive: can't be used on anything but a type " ++
"constructor of an algebraic data type")
derive :: [Name] -> Q [Dec]
derive names = do
decss <- mapM deriveOne names
return (concat decss)
deriveDec :: [Dec] -> Q [Dec]
deriveDec decs = do
decss <- mapM deriveOneDec decs
return (concat decss)
deriveData :: [Name] -> Q [Dec]
deriveData names = do
decss <- mapM deriveOneData names
return (concat decss)
deriveTypeable :: [Name] -> Q [Dec]
deriveTypeable names = do
decss <- mapM deriveOneTypeable names
return (concat decss)
deriveOneTypeable :: Name -> Q [Dec]
deriveOneTypeable n =
do info' <- reify n
case info' of
TyConI i -> do
(name, param, _, _) <- typeInfo ((return i) :: Q Dec)
t <- deriveTypeablePrim name (length param)
return t
_ -> error ("derive: can't be used on anything but a type " ++
"constructor of an algebraic data type")
deriveMinimalOne :: Name -> Q [Dec]
deriveMinimalOne n =
do info' <- reify n
case info' of
TyConI i -> do
(name, param, _, _) <- typeInfo ((return i) :: Q Dec)
t <- deriveTypeablePrim name (length param)
d <- deriveMinimalData name (length param)
return $ t ++ d
_ -> error ("deriveMinimal: can't be used on anything but a " ++
"type constructor of an algebraic data type")
deriveMinimal :: [Name] -> Q [Dec]
deriveMinimal names = do
decss <- mapM deriveMinimalOne names
return (concat decss)