module Control.THEff.TH.Internal(mkEff) where
import Language.Haskell.TH
import Data.List
import Data.Either
import Data.Maybe
data DescrEff = DescrEff { dModule :: String
, dName :: String
, dEffModule :: String
, dEffName :: String
, dType :: Maybe Type
} deriving Show
splitLast:: Char -> String -> (String,String)
splitLast c s = case findIndices (c ==) s of
[] -> ("",s)
ixs -> splitAt (last ixs +1) s
getModuleAndName :: Name -> (String,String)
getModuleAndName = splitLast '.' . show
parseCon :: Con -> Either String DescrEff
parseCon (NormalC n [(NotStrict, AppT t@(AppT (ConT e) _) _ )]) =
if not (null en) && last en == '\'' then
Right $ DescrEff nm nn' em (init en) (if en == "Lift'" then Nothing else Just t)
else Left $ "Incorrect type name : " ++ en
where (nm,nn) = getModuleAndName n
(_,nn') = splitLast '_' nn
(em,en) = getModuleAndName e
parseCon _ = Left "Incorrect subtype"
mkN_E :: String -> String -> Name
mkN_E prfx eff = mkName $ concat [ prfx, "_", eff]
mkEffName' :: DescrEff -> Name
mkEffName' DescrEff{..} = mkName $ concat [ dEffModule, dEffName, "'"]
mkPrimName :: String -> Name
mkPrimName = mkName . (++ "'")
mkTypePrim :: String -> [DescrEff] -> Q Dec
mkTypePrim newType lst = do
m <- newName "m"
e <- newName "e"
let kinds = [KindedTV m (AppT (AppT ArrowT StarT) StarT), KindedTV e StarT]
let n = mkPrimName newType
let mkc = mkCon (VarT m) (VarT e) newType
return $
case lst of
[d] -> NewtypeD [] n kinds (mkc d) []
_ -> DataD [] n kinds (map mkc lst) []
mkCon :: Type -> Type -> String -> DescrEff -> Con
mkCon m e newType d@DescrEff{..} =
NormalC (mkN_E newType dName) [(NotStrict, AppT
( case dType of
(Just t) -> t
_ -> AppT (ConT $ mkEffName' d) m
) e )]
mkTypeMain :: String -> String -> Name -> Name -> Type -> Q Dec
mkTypeMain thisMdl newType newTypeName effName argT = do
m <- newName "m"
a <- newName "a"
let n = mkName newType
let t' = mkPrimName $ thisMdl ++ newType
let u = mkName $ "un" ++ newType
return $
NewtypeD [] n [KindedTV m (AppT (AppT ArrowT StarT) StarT),
KindedTV a StarT]
(RecC n
[(u,NotStrict,
AppT (AppT (AppT (AppT
(AppT (ConT effName) (VarT m))
(AppT (AppT (ConT newTypeName) (VarT m))
(VarT a))
)
(ConT t')
)
argT
)
(VarT a)
)]) []
lookEff :: (String -> Q (Maybe Name)) -> String -> Q Name
lookEff f n = do
m <- f n
case m of
(Just x) -> return x
_ -> fail $ concat ["mkEff: ", n, " not found"]
lookEffType :: String -> Q Name
lookEffType = lookEff lookupTypeName
lookEffValue :: String -> Q Name
lookEffValue = lookEff lookupValueName
lookEffFn :: String -> String -> Q Name
lookEffFn mdl eff = lookEffValue $ concat [mdl, "eff", eff]
data InstnType = InstnAction | InstnOuter
mkInstn :: String -> String -> Name -> Name -> InstnType -> DescrEff -> Q Dec
mkInstn thisMdl newType newTypeName thisEffName it DescrEff{..} = do
let thisEff = show thisEffName
c <- lookEffType (if isJust dType then "EffClass" else "EffClassM")
m <- newName "m"
a <- newName "a"
x <- newName "x"
let fullNewType = thisMdl ++ newType
return $ InstanceD [] (AppT
(case dType of
(Just (AppT effT argT)) -> AppT (AppT (ConT c) effT) argT
_ -> AppT (ConT c) (VarT m)
)
(AppT (AppT (ConT newTypeName) (VarT m)) (VarT a)))
[FunD (mkName (if isJust dType then "toEff" else "toEffM"))
[Clause [VarP x] (NormalB (AppE (ConE newTypeName)
(case it of
InstnAction -> AppE (ConE (mkName $ concat [thisEff,"Action"]))
(VarE x)
InstnOuter -> AppE (ConE (mkName $ concat [thisEff,"Outer"]))
(AppE (ConE (mkN_E fullNewType dName)) (VarE x))
))) []]]
is2ar :: Name -> Q Bool
is2ar t = do
(TyConI d) <- reify t
return (case d of
(TySynD _ [_,_] _) -> True
(NewtypeD [] _ [_,_] _ _) -> True
(DataD _ _ [_,_] _ _) -> True
_ -> False
)
mkRunFun :: String -> String -> Name -> Name -> Type -> Name -> [DescrEff] -> DecsQ
mkRunFun thisMdl newType newTypeName thisEffName argT outerName ds = do
mf <- lookEffValue ">>="
eff <- lookEffType "Eff"
let (tm,tn) = getModuleAndName thisEffName
runEffThisEffName <- lookEffValue $ concat [tm,"runEff",tn]
ri <- reify runEffThisEffName
let useArg = case ri of
(VarI _ (ForallT _ _
(AppT _
(AppT _
(AppT _
(AppT
(AppT ArrowT _)
(AppT
(AppT ArrowT (AppT (AppT (ConT _) (VarT _)) (VarT _)))
(AppT (VarT _) _
))))))) _ _ ) -> True
_ -> False
resTName <- lookEffType $ concat [tm,tn,"ResT"]
m <- newName "m"
a <- newName "a"
b <- newName "b"
h <- newName "h"
twoArgResT <- is2ar resTName
let fullNewType = thisMdl ++ newType
fnName = mkName $ "run" ++ newType
uTypeName = mkName $ concat [thisMdl,"un",newType]
resT = AppT (ConT resTName) (VarT a)
resT2 = if twoArgResT then AppT resT argT else resT
t = AppT (AppT ArrowT (AppT (AppT (ConT eff) (AppT (AppT (ConT newTypeName)
(VarT m)) (VarT a))) (VarT a)))
(case on of
"Lift" -> AppT (VarT m) resT2
"NoEff" -> resT2
_ -> (AppT (AppT (ConT eff) (AppT (AppT (ConT outerName) (VarT m)) (VarT b)))
resT2) )
argsAndResT <- if useArg then do
argTName <- lookEffType $ concat [tm,tn,"ArgT"]
twoArgT <- is2ar argTName
let a1T = AppT (ConT argTName) argT
aT = if twoArgT then AppT a1T (VarT a) else a1T
return $ AppT (AppT ArrowT aT) t
else return t
case ds of
[] -> do
let isLift = on == "Lift"
g <- newName "g"
e <- lookEffFn om on
cx <- if isLift then do
mnd <- lookEffType "Monad"
return [AppT (ConT mnd) (VarT m)]
else return []
let outer = if isLift then
(LamE [ConP (mkN_E fullNewType on) [VarP h]] (AppE (VarE mf)
(AppE (VarE e) (VarE h))))
else VarE e
cls = if useArg then [VarP b,VarP g] else [VarP g]
runeff = AppE (AppE (AppE (VarE runEffThisEffName) outer) (ConE newTypeName))
(VarE uTypeName)
mainBd = if useArg then AppE (AppE runeff (VarE b)) (VarE g)
else AppE runeff (VarE g)
runOuterEffName <- lookEffValue $ concat [om,"run",on]
return $ [SigD fnName (ForallT [PlainTV m,PlainTV a] cx argsAndResT),
FunD fnName [Clause cls (NormalB (AppE (VarE runOuterEffName) mainBd)) []]]
_ -> do
let mkMatch DescrEff{..} = do
g <- newName "g"
e <- lookEffFn dEffModule dEffName
return $ Match (ConP (mkN_E fullNewType dName) [VarP g])
(NormalB (AppE (VarE e) (VarE g))) []
ms <- mapM mkMatch ds
return [SigD fnName (ForallT [PlainTV m,PlainTV a,PlainTV b] [] argsAndResT ),
ValD (VarP fnName) (NormalB (AppE (AppE (AppE (VarE runEffThisEffName)
(LamE [VarP h] (AppE (VarE mf) (CaseE (VarE h) ms)))) (ConE newTypeName))
(VarE uTypeName))) []]
where (om,on) = getModuleAndName outerName
mkEff :: String
-> Name
-> Name
-> Name
-> DecsQ
mkEff newType effName effArg outt = do
loc <- location
let thisMdl = loc_module loc ++ "."
fullNewType = thisMdl ++ newType
newTypeName = mkName fullNewType
argT = ConT effArg
if on `elem` ["Lift","NoEff"]
then do
let isLift = on == "Lift"
o = DescrEff "" on om on Nothing
dPrim <- mkTypePrim newType (if isLift then [o] else [])
dMain <- mkTypeMain thisMdl newType newTypeName effName argT
dMainInst <- mkInstn thisMdl newType newTypeName effName
InstnAction $ mkDescrEff "" "" "" "" effName argT
dOuterInst <- mkInstn thisMdl newType newTypeName effName InstnOuter o
dRun <- mkRunFun thisMdl newType newTypeName effName argT outt []
let r = if isLift then dOuterInst:dRun else dRun
return $ dPrim:dMain:dMainInst:r
else do
oi <- reify outt
case oi of
TyConI (NewtypeD [] _ [_,_] (RecC (ocmp -> True)
[(_,NotStrict,
AppT (AppT (AppT (AppT
(AppT (ConT te) _ ) _
)
(ConT tep)
)
argt
)
_
)]) []) -> do
opi <- reify tep
let ~(TyConI dec) = opi
let lst = case dec of
(NewtypeD [] _ [_,_] c []) -> [parseCon c]
(DataD [] _ [_,_] cl []) -> map parseCon cl
_ -> [Left $ "Incorrect type " ++ show tep]
case lefts lst of
(e:_) -> fail $ "mkEff: " ++ e
[] -> let l = rights lst
(tm,tn) = getModuleAndName te
o = mkDescrEff om on tm tn te argt
l1 = o:l
in do
dPrim <- mkTypePrim newType l1
dMain <- mkTypeMain thisMdl newType newTypeName effName argT
dMainInst <- mkInstn thisMdl newType newTypeName effName
InstnAction $ mkDescrEff "" "" "" "" effName argT
dOuterInsts <- mapM
(mkInstn thisMdl newType newTypeName effName InstnOuter)
l1
dRun <- mkRunFun thisMdl newType newTypeName effName
argT outt l1
return $ dPrim:dMain:dMainInst:(dOuterInsts ++ dRun)
_ -> fail "mkEff: Expected newtype in forth argument or ''Lift or ''NoEff"
where (om,on) = getModuleAndName outt
soutt = show outt
ocmp = (soutt ==) . show
mkDescrEff oM oN tM tN tE aT = DescrEff oM oN tM tN (Just $ AppT (ConT
(mkName $ show tE ++ "'")) aT)