module Generics.EMGM.Common.Derive.Instance (
#ifndef __HADDOCK__
mkRepInst,
mkFRepInst,
mkFRep2Inst,
mkFRep3Inst,
mkBiFRep2Inst,
mkRepCollectInst,
#endif
) where
#ifndef __HADDOCK__
import Data.List (nub)
import Language.Haskell.TH
import Generics.EMGM.Common.Base
import Generics.EMGM.Common.Base2
import Generics.EMGM.Common.Base3
import Generics.EMGM.Common.Derive.Common
import Generics.EMGM.Functions.Collect
data RepOpt = OptRep | OptFRep Name | OptFRep2 Name | OptFRep3 Name | OptBiFRep2 Name Name
deriving (Eq, Show)
data RepNames
= RepNames
{ genericCN' :: Name
, rintN' :: Name
, rintegerN' :: Name
, rfloatN' :: Name
, rdoubleN' :: Name
, rcharN' :: Name
, runitN' :: Name
, rsumN' :: Name
, rprodN' :: Name
, rconN' :: Name
, rtypeN' :: Name
, repCN' :: Name
, repN' :: Name
}
repNames :: RepOpt -> RepNames
repNames OptRep = RepNames ''Generic 'rep 'rep 'rep 'rep 'rep 'runit 'rsum 'rprod 'rcon 'rtype ''Rep 'rep
repNames (OptFRep _) = RepNames ''Generic 'rint 'rinteger 'rfloat 'rdouble 'rchar 'runit 'rsum 'rprod 'rcon 'rtype ''FRep 'frep
repNames (OptFRep2 _) = RepNames ''Generic2 'rint2 'rinteger2 'rfloat2 'rdouble2 'rchar2 'runit2 'rsum2 'rprod2 'rcon2 'rtype2 ''FRep2 'frep2
repNames (OptFRep3 _) = RepNames ''Generic3 'rint3 'rinteger3 'rfloat3 'rdouble3 'rchar3 'runit3 'rsum3 'rprod3 'rcon3 'rtype3 ''FRep3 'frep3
repNames (OptBiFRep2 _ _) = RepNames ''Generic2 'rint2 'rinteger2 'rfloat2 'rdouble2 'rchar2 'runit2 'rsum2 'rprod2 'rcon2 'rtype2 ''BiFRep2 'bifrep2
genericCN, rintN, rintegerN, rfloatN, rdoubleN, rcharN, runitN, rsumN, rprodN, rconN, rtypeN, repCN, repN :: RepOpt -> Name
genericCN = genericCN' . repNames
rintN = rintN' . repNames
rintegerN = rintegerN' . repNames
rfloatN = rfloatN' . repNames
rdoubleN = rdoubleN' . repNames
rcharN = rcharN' . repNames
runitN = runitN' . repNames
rsumN = rsumN' . repNames
rprodN = rprodN' . repNames
rconN = rconN' . repNames
rtypeN = rtypeN' . repNames
repCN = repCN' . repNames
repN = repN' . repNames
conTypeExpName :: Name -> RepOpt -> Name
conTypeExpName typeName =
case nameBase typeName of
"Int" -> rintN
"Integer" -> rintegerN
"Float" -> rfloatN
"Double" -> rdoubleN
"Char" -> rcharN
n -> error $ "Error! Unsupported constant type: " ++ n
typeUnknownError :: Type -> a
typeUnknownError t = error $ "Error! Unsupported type: " ++ pprint t
var1Exp :: Name -> RepOpt -> Type -> Exp
var1Exp typeVarName opt = toExp
where
toExp (AppT (ConT _) arg) = AppE (VarE (repN opt)) (toExp arg)
toExp (ConT typeName) = VarE (conTypeExpName typeName opt)
toExp (VarT _) = VarE typeVarName
toExp t = typeUnknownError t
var2Exp :: Name -> Name -> RepOpt -> DT -> Type -> Exp
var2Exp name1 name2 opt dt = toExp
where
toExp (AppT (AppT (ConT _) arg1) arg2) = app2 arg1 arg2
toExp (ConT typeName) = VarE (conTypeExpName typeName opt)
toExp t@(VarT name) | name == tv1 = VarE name1
| name == tv2 = VarE name2
| otherwise = typeUnknownError t
toExp t = typeUnknownError t
tv1:tv2:_ = tvars dt
app2 arg1 arg2 = AppE (AppE (VarE (repN opt)) (toExp arg1)) (toExp arg2)
varRepExp :: RepOpt -> DT -> Type -> Exp
varRepExp opt dt t =
case opt of
OptRep -> VarE (repN opt)
OptFRep name -> var1Exp name opt t
OptFRep2 name -> var1Exp name opt t
OptFRep3 name -> var1Exp name opt t
OptBiFRep2 name1 name2 -> var2Exp name1 name2 opt dt t
repLamE :: RepOpt -> Exp -> Exp
repLamE OptRep = id
repLamE (OptFRep name) = LamE [VarP name]
repLamE (OptFRep2 name) = LamE [VarP name]
repLamE (OptFRep3 name) = LamE [VarP name]
repLamE (OptBiFRep2 name1 name2) = LamE [VarP name1, VarP name2]
typeArity :: RepOpt -> Int
typeArity OptRep = 0
typeArity (OptFRep _) = 1
typeArity (OptFRep2 _) = 1
typeArity (OptFRep3 _) = 1
typeArity (OptBiFRep2 _ _) = 2
rtypeE :: RepOpt -> Name -> Exp -> Exp
rtypeE opt epName sopE =
case opt of
OptRep -> appToSop ep1
(OptFRep _) -> appToSop ep1
(OptFRep2 _) -> appToSop ep2
(OptFRep3 _) -> appToSop ep3
(OptBiFRep2 _ _) -> appToSop ep2
where
appToEp e = AppE e (VarE epName)
appToSop eps = AppE eps sopE
ep1 = appToEp (VarE (rtypeN opt))
ep2 = appToEp ep1
ep3 = appToEp ep2
repSopE :: RepOpt -> DT -> Exp
repSopE opt dt = mkSopDT inject unit mkSum mkProd wrapProd dt
where
mkSum = AppE . AppE (VarE $ rsumN opt)
mkProd = AppE . AppE (VarE $ rprodN opt)
unit = VarE $ runitN opt
inject = varRepExp opt dt
wrapProd ncon = AppE (AppE (VarE (rconN opt)) (VarE (cdescr ncon)))
mkRepD :: RepOpt -> Name -> DT -> Dec
mkRepD opt epName dt = ValD (VarP (repN opt)) (NormalB (lamExp rtypeExp)) []
where
sopExp = repSopE opt dt
rtypeExp = rtypeE opt epName sopExp
lamExp = repLamE opt
mkGenericT :: RepOpt -> Type -> Type
mkGenericT opt = AppT (ConT (genericCN opt))
mkRepT :: RepOpt -> Type -> Type -> Type
mkRepT opt funType = AppT (AppT (ConT (repCN opt)) funType)
mkRepInstCxt :: RepOpt -> Type -> [NCon] -> Cxt
mkRepInstCxt opt funType = insGeneric . checkRepOpt . addRepCxt
where
addRepCxt = nub . toRepCxt . toConArgTypes
toConArgTypes = concatMap cargtypes
toRepCxt = map $ mkRepT opt funType
checkRepOpt = if opt == OptRep then id else const []
insGeneric = (:) $ mkGenericT opt funType
dropLast :: Int -> [a] -> [a]
dropLast n xs = if len > n then take (len n) xs else []
where
len = length xs
mkAppliedType :: RepOpt -> DT -> Type
mkAppliedType opt dt = appTypeCon varTypes
where
appTypeCon = foldl AppT (ConT (tname dt)) . dropLast (typeArity opt)
varTypes = map VarT (tvars dt)
mkRepInstT :: RepOpt -> DT -> Type -> Type
mkRepInstT opt dt funType = mkRepT opt funType (mkAppliedType opt dt)
mkRepInstWith :: RepOpt -> Name -> Name -> DT -> Dec
mkRepInstWith opt epName g dt = InstanceD cxt' typ [dec]
where
gVar = VarT g
cxt' = mkRepInstCxt opt gVar (ncons dt)
typ = mkRepInstT opt dt gVar
dec = mkRepD opt epName dt
mkRepInst :: Name -> Name -> DT -> Dec
mkRepInst = mkRepInstWith OptRep
mkFRepInst :: Name -> Name -> Name -> DT -> Dec
mkFRepInst = mkRepInstWith . OptFRep
mkFRep2Inst :: Name -> Name -> Name -> DT -> Dec
mkFRep2Inst = mkRepInstWith . OptFRep2
mkFRep3Inst :: Name -> Name -> Name -> DT -> Dec
mkFRep3Inst = mkRepInstWith . OptFRep3
mkBiFRep2Inst :: Name -> Name -> Name -> Name -> DT -> Dec
mkBiFRep2Inst ra rb = mkRepInstWith (OptBiFRep2 ra rb)
mkRepCollectInst :: DT -> Q Dec
mkRepCollectInst dt = do
let t = mkAppliedType OptRep dt
let typ = mkRepInstT OptRep dt (AppT (ConT ''Collect) t)
e <- [|Collect (\x -> [x])|]
let dec = ValD (VarP 'rep) (NormalB e) []
return $ InstanceD [] typ [dec]
#endif