module Data.Derive.TopDown.StandaloneDerive (deriveTopdown,derivings,generic_instances, instances ) where
import Language.Haskell.TH
import Control.Monad.State
import Control.Monad.Trans (lift)
import Data.List (foldl')
import qualified GHC.Generics as G
deriveTopdown :: Name
-> Name
-> Bool
-> Q [Dec]
deriveTopdown cn tn g = evalStateT (gen cn tn g) []
derivings ::Bool -> [Name]
-> Name
-> Q [Dec]
derivings g cnms t = fmap concat (sequence $ map (\x -> deriveTopdown x t g) cnms)
instances = False
generic_instances = True
gen :: Name -> Name -> Bool -> StateT [Type] Q [Dec]
gen cn tn withGeneric = do
(tvbs,cons) <- lift $ getTyVarCons tn
let typeNames = map getTVBName tvbs
instanceType <- lift $ foldl' appT (conT tn) $ map varT typeNames
let derive_context = (map (AppT (ConT cn)) (map VarT typeNames)) ++
if withGeneric then map (AppT (ConT ''G.Generic)) (map VarT typeNames)
else []
let derive_context_in_tuple = foldl1 AppT $ (TupleT (length derive_context)) : derive_context
isMember <- if tvbs == []
then lift $ isInstance cn [instanceType]
else lift $ isInstance cn [ForallT tvbs [] instanceType]
table <- get
if isMember || elem instanceType table
then return []
else do
let c = [StandaloneDerivD [derive_context_in_tuple] (AppT (ConT cn) instanceType)]
modify (instanceType:)
let names = concatMap getCompositeType cons
xs <- mapM (\n -> gen cn n withGeneric) names
return $ concat xs ++ c
getCompositeType :: Con -> [Name]
getCompositeType (NormalC n sts) = concatMap getTypeNames (map snd sts)
getCompositeType (RecC n vars) = concatMap getTypeNames (map third vars)
getCompositeType (InfixC st1 n st2) = concatMap getTypeNames [snd st1 , snd st2]
getCompositeType (ForallC tvbs cxt con) = getCompositeType con
getTypeNames :: Type -> [Name]
getTypeNames (ForallT tvbs cxt t) = getTypeNames t
getTypeNames (ConT n) = [n]
getTypeNames (AppT t1 t2) = getTypeNames t1 ++ getTypeNames t2
getTypeNames _ = []
third (a,b,c) = c
applyConT :: [Type] -> Type
applyConT = foldr1 AppT
getTVBName :: TyVarBndr -> Name
getTVBName (PlainTV name ) = name
getTVBName (KindedTV name _) = name
getTyVarCons :: Name -> Q ([TyVarBndr], [Con])
getTyVarCons name = do
info <- reify name
case info of
TyConI dec ->
case dec of
DataD _ _ tvbs cons _ -> return (tvbs,cons)
NewtypeD _ _ tvbs con _ -> return (tvbs,[con])
TySynD _ vars type' -> undefined
_ -> error "must be data, newtype definition or type synonym!"
_ -> error "bad type name, quoted name is not a type!"