packages feed

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]

--------------------------------------------------------------------------------