generic-deriving-1.12.2: src/Generics/Deriving/TH/Pre4_9.hs
{-# LANGUAGE CPP #-}
{- |
Module : Generics.Deriving.TH.Pre4_9
Copyright : (c) 2008--2009 Universiteit Utrecht
License : BSD3
Maintainer : generics@haskell.org
Stability : experimental
Portability : non-portable
Template Haskell machinery for the proxy datatype variant of GHC generics
used up until @base-4.9@.
-}
module Generics.Deriving.TH.Pre4_9 (
deriveMeta
, deriveData
, deriveConstructors
, deriveSelectors
, mkMetaDataType
, mkMetaConsType
, mkMetaSelType
, SelStrictInfo
, reifySelStrictInfo
) where
import Data.List (intercalate)
import Data.Maybe (fromMaybe)
import Generics.Deriving.TH.Internal
import Language.Haskell.TH.Datatype
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Syntax
-- | Given the type and the name (as string) for the type to derive,
-- generate the 'Data' instance, the 'Constructor' instances, and the 'Selector'
-- instances.
deriveMeta :: Name -> Q [Dec]
deriveMeta n =
do a <- deriveData n
b <- deriveConstructors n
c <- deriveSelectors n
return (a ++ b ++ c)
-- | Given a datatype name, derive a datatype and instance of class 'Datatype'.
deriveData :: Name -> Q [Dec]
deriveData = dataInstance
-- | Given a datatype name, derive datatypes and
-- instances of class 'Constructor'.
deriveConstructors :: Name -> Q [Dec]
deriveConstructors = constrInstance
-- | Given a datatype name, derive datatypes and instances of class 'Selector'.
deriveSelectors :: Name -> Q [Dec]
deriveSelectors = selectInstance
dataInstance :: Name -> Q [Dec]
dataInstance n = do
i <- reifyDataInfo n
case i of
Left _ -> return []
Right (n', _, _, dv) -> mkInstance n' dv
where
mkInstance n' dv = do
ds <- mkDataData dv n'
is <- mkDataInstance dv n'
return $ [ds,is]
constrInstance :: Name -> Q [Dec]
constrInstance n = do
i <- reifyDataInfo n
case i of
Left _ -> return []
Right (n', _, cons, dv) -> mkInstance n' cons dv
where
mkInstance n' cons dv = do
ds <- mapM (mkConstrData dv n') cons
is <- mapM (mkConstrInstance dv n') cons
return $ ds ++ is
selectInstance :: Name -> Q [Dec]
selectInstance n = do
i <- reifyDataInfo n
case i of
Left _ -> return []
Right (n', _, cons, dv) -> mkInstance n' cons dv
where
mkInstance n' cons dv = do
ds <- mapM (mkSelectData dv n') cons
is <- mapM (mkSelectInstance dv n') cons
return $ concat (ds ++ is)
mkDataData :: DatatypeVariant_ -> Name -> Q Dec
mkDataData dv n =
dataD (cxt []) (genName dv [n]) []
#if MIN_VERSION_template_haskell(2,11,0)
Nothing [] (cxt [])
#else
[] []
#endif
mkConstrData :: DatatypeVariant_ -> Name -> ConstructorInfo -> Q Dec
mkConstrData dv dt
(ConstructorInfo { constructorName = n
, constructorVars = vars
, constructorContext = ctxt
}) = do
checkExistentialContext n vars ctxt
dataD (cxt []) (genName dv [dt, n]) []
#if MIN_VERSION_template_haskell(2,11,0)
Nothing [] (cxt [])
#else
[] []
#endif
mkSelectData :: DatatypeVariant_ -> Name -> ConstructorInfo -> Q [Dec]
mkSelectData dv dt
(ConstructorInfo { constructorName = n
, constructorVariant = cv
}) =
case cv of
NormalConstructor -> return []
InfixConstructor -> return []
RecordConstructor fs -> return (map one fs)
where one f = DataD [] (genName dv [dt, n, f]) []
#if MIN_VERSION_template_haskell(2,11,0)
Nothing
#endif
[] []
mkDataInstance :: DatatypeVariant_ -> Name -> Q Dec
mkDataInstance dv n =
instanceD (cxt []) (appT (conT datatypeTypeName) (mkMetaDataType dv n)) $
[ funD datatypeNameValName [clause [wildP] (normalB (stringE (nameBase n))) []]
, funD moduleNameValName [clause [wildP] (normalB (stringE name)) []]
]
#if MIN_VERSION_base(4,7,0)
++ if isNewtypeVariant dv
then [funD isNewtypeValName [clause [wildP] (normalB (conE trueDataName)) []]]
else []
#endif
where
name = fromMaybe (error "Cannot fetch module name!") (nameModule n)
liftFixity :: Fixity -> Q Exp
liftFixity (Fixity n a) = conE infixDataName
`appE` liftAssociativity a
`appE` lift n
liftAssociativity :: FixityDirection -> Q Exp
liftAssociativity InfixL = conE leftAssociativeDataName
liftAssociativity InfixR = conE rightAssociativeDataName
liftAssociativity InfixN = conE notAssociativeDataName
mkConstrInstance :: DatatypeVariant_ -> Name
-> ConstructorInfo -> Q Dec
mkConstrInstance dv dt
(ConstructorInfo { constructorName = n
, constructorVars = vars
, constructorContext = ctxt
, constructorVariant = cv
}) = do
checkExistentialContext n vars ctxt
case cv of
NormalConstructor -> mkConstrInstanceWith dv dt n False False []
InfixConstructor -> do
i <- reify n
#if MIN_VERSION_template_haskell(2,11,0)
fi <- case i of
DataConI{} -> fromMaybe defaultFixity `fmap` reifyFixity n
#else
let fi = case i of
DataConI _ _ _ f -> f
#endif
_ -> error $ "Not a data constructor name: " ++ show n
mkConstrInstanceWith dv dt n False True
[funD conFixityValName [clause [wildP] (normalB (liftFixity fi)) []]]
RecordConstructor _ ->
mkConstrInstanceWith dv dt n True False
[funD conIsRecordValName [clause [wildP] (normalB (conE trueDataName)) []]]
mkConstrInstanceWith :: DatatypeVariant_
-> Name
-> Name
-> Bool
-> Bool
-> [Q Dec]
-> Q Dec
mkConstrInstanceWith dv dt n isRecord isInfix extra =
instanceD
(cxt [])
(appT (conT constructorTypeName) (mkMetaConsType dv dt n isRecord isInfix))
(funD conNameValName [clause [wildP] (normalB (stringE (nameBase n))) []] : extra)
mkSelectInstance :: DatatypeVariant_ -> Name
-> ConstructorInfo -> Q [Dec]
mkSelectInstance dv dt
(ConstructorInfo { constructorName = n
, constructorVariant = cv
}) =
case cv of
NormalConstructor -> return []
InfixConstructor -> return []
RecordConstructor fs -> mapM one fs
where
one :: Name -> Q Dec
one f =
instanceD (cxt []) (appT (conT selectorTypeName) (mkMetaSelType dv dt n (Just f) ()))
[funD selNameValName [clause [wildP]
(normalB (litE (stringL (nameBase f)))) []]]
genName :: DatatypeVariant_ -> [Name] -> Name
genName dv ns
= mkName
. showsDatatypeVariant dv
. intercalate "_"
. consQualName
$ map (sanitizeName . nameBase) ns
where
consQualName :: [String] -> [String]
consQualName = case ns of
[] -> id
n:_ -> (showNameQual n :)
mkMetaDataType :: DatatypeVariant_ -> Name -> Q Type
mkMetaDataType dv n = conT $ genName dv [n]
mkMetaConsType :: DatatypeVariant_ -> Name -> Name -> Bool -> Bool -> Q Type
mkMetaConsType dv dt n _ _ = conT $ genName dv [dt, n]
mkMetaSelType :: DatatypeVariant_ -> Name -> Name -> Maybe Name
-> SelStrictInfo -> Q Type
mkMetaSelType dv dt n (Just f) () = conT $ genName dv [dt, n, f]
mkMetaSelType _ _ _ Nothing () = conT noSelectorTypeName
type SelStrictInfo = ()
reifySelStrictInfo :: Name -> [FieldStrictness] -> Q [SelStrictInfo]
reifySelStrictInfo _ bangs = return (map (const ()) bangs)