hoop-0.3.0.0: src/Language/MSH/CodeGen/Invoke.hs
--------------------------------------------------------------------------------
-- Monadic State Hierarchies --
-- Copyright 2013-2019 Michael B. Gale (m.gale@warwick.ac.uk) --
--------------------------------------------------------------------------------
module Language.MSH.CodeGen.Invoke (genInvoke) where
--------------------------------------------------------------------------------
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Language.MSH.StateDecl
import Language.MSH.CodeGen.Shared
import Language.MSH.CodeGen.SharedInstance (genRunStateT)
--------------------------------------------------------------------------------
-- | Generates the typing for the `invoke' function.
genInvokeDecl :: [TyVarBndr] -> StateDecl -> Q Dec
genInvokeDecl tyvars StateDecl{..} = do
-- initialise some fresh type variables
o' <- newName "o'"
d' <- newName "d'"
r <- newName "r"
let
objName = mkName stateName
identity = mkName "Identity"
objTy = appN (ConT objName) stateParams
-- the name of the invoke function (e.g. _Expr_invoke)
name = mkName $ "_" ++ stateName ++ "_invoke"
-- the name of the corresponding type class (e.g. ExprLike)
cname = mkName $ stateName ++ "Like"
-- the name of the corresponding monad (TODO: apply tyvars )
base = appN (ConT (mkName $ stateName ++ "M")) (stateParams)
-- type class constraints (the context) on the type of the invoke function
ctx = [ foldl AppT (ConT cname) ([ConT objName, ConT $ mkName $ stateName ++ "State", ConT identity] ++ [VarT $ mkName n | n <- stateParams])
, foldl AppT (ConT cname) ([VarT o', VarT d', base] ++ [VarT $ mkName n | n <- stateParams])
]
-- ovs = appN (VarT o) tyvars
ovs' = appN (VarT o') stateParams
sigma = ovs' `arr` (ovs' `arr` AppT base (tuple [VarT r, ovs'])) `arr`
objTy `arr` AppT (ConT identity) (tuple [VarT r, objTy, ovs'])
-- the type of the invoke function
ty = ForallT ([PlainTV o', PlainTV d', PlainTV r] ++ [PlainTV (mkName v) | v <- stateParams]) ctx sigma
return $ SigD name ty
-- | Generates the implementation of the `_C_invoke' function.
-- The purpose of the `_C_invoke' functions is to allow a sub-class to
-- pass an arbitrary method to the super-class. It works as follows:
--
genInvokeDef :: [TyVarBndr] -> StateDecl -> Q Dec
genInvokeDef tyvars decl = case stateParent decl of
Nothing -> do
s <- newName "s"
f <- newName "f"
o <- newName "o"
r <- newName "r"
d' <- newName "d'"
s' <- newName "s'"
let
name = mkName $ "_" ++ stateName decl ++ "_invoke"
fn = mkName $ "_" ++ stateName decl ++ "_data"
ps = [VarP s, VarP f, VarP o]
runs = BindS (TupP [TupP [VarP r, VarP s'], VarP d']) (genRunStateT (AppE (VarE f) (VarE s)) (AppE (VarE $ mkName "extractData") (VarE o)))
rets = AppE (VarE $ mkName "return") (TupE [VarE r, RecUpdE (VarE o) [(fn,VarE d')], VarE s'])
body = NormalB $ DoE [runs, NoBindS rets]
return $ FunD name [Clause ps body []]
Just pDecl -> do
subObj <- newName "subObj"
fun <- newName "fun"
obj <- newName "obj"
p <- newName "p"
d <- newName "d"
r <- newName "r"
subObj' <- newName "subObj'"
p' <- newName "p'"
obj' <- newName "obj'"
r0 <- newName "r0"
subObj0 <- newName "subObj0"
d0 <- newName "d0"
let
name = mkName $ "_" ++ stateName decl ++ "_invoke"
pname = mkName $ "_" ++ stateName pDecl ++ "_invoke"
endCtr = mkName $ stateName decl ++ "End"
ps = [VarP subObj, VarP fun, AsP obj (ConP endCtr [VarP p, VarP d])]
ed = AppE (VarE $ mkName "extractData") (VarE obj')
innerRun = BindS (TupP [TupP [VarP r0, VarP subObj0], VarP d0]) (genRunStateT (AppE (VarE fun) (VarE subObj)) (VarE d))
innerRet = appEs (VarE $ mkName "return") [TupE [TupE [VarE r0, VarE subObj0], appEs (ConE endCtr) [VarE p, VarE d0]]]
wrapper = LamE [WildP] $ DoE [innerRun, NoBindS innerRet]
subInvoke = appEs (VarE $ pname) [VarE obj, wrapper, VarE p]
runs = BindS (TupP [TupP [VarP r, VarP subObj'], VarP p', VarP obj']) subInvoke
rets = AppE (VarE $ mkName "return") (TupE [VarE r, appEs (ConE endCtr) [VarE p', ed], VarE subObj'])
body = NormalB $ DoE [runs, NoBindS rets]
return $ FunD name [Clause ps body []]
-- | `genInvoke` @decl@ generates the code for the @_C_invoke@ function of
-- a state class @decl@.
genInvoke :: [TyVarBndr] -> StateDecl -> Q [Dec]
genInvoke tyvars decl = do
dec <- genInvokeDecl tyvars decl
def <- genInvokeDef tyvars decl
return [dec, def]
--------------------------------------------------------------------------------