packages feed

rebound-0.1.0.0: examples/SystemF.hs

-- | This is an example of the use of the library with two separate variable types
module SystemF where

{- One issue with this example is that we only store one sort of environment at each binder. 
   However, terms are subject to two different forms of substitution --- either for terms or types.
   So, applying the "wrong" sort through a binder means that we don't gain any advantage from 
   the caching --- we need to bind and unbind the to propagate.

-}

import Prelude hiding (lookup)
import Rebound
import Rebound.Bind.Single


data Ty (n :: Nat) where
    TVar :: Fin n -> Ty n
    TAll :: Bind Ty Ty n -> Ty n
    TArr :: Ty n -> Ty n -> Ty n
      deriving (Eq)

-- swap the order of the scopes so that we can talk about 
-- substituting a type inside of an expression
newtype TyExp n m = TyExp { unTyExp :: Exp m n }

data Exp (m :: Nat) (n :: Nat) where
    EVar  :: Fin n -> Exp m n
    ELam  :: Ty m -> Bind (Exp m) (Exp m) n -> Exp m n 
    EApp  :: Exp m n -> Exp m n -> Exp m n
    ETLam :: Bind Ty (TyExp n) m -> Exp m n
    ETApp :: Exp m n -> Ty m -> Exp m n

instance SubstVar Ty where
    var = TVar 
instance Subst Ty Ty where
    applyE r (TVar x) = applyEnv r x
    applyE r (TAll b) = TAll (applyE r b)
    applyE r (TArr t1 t2) = TArr (applyE r t1) (applyE r t2)

instance SubstVar (Exp m) where
    var = EVar

-- apply type substitution to an expression, using the newtype
substTy :: Env Ty m1 m2 -> Exp m1 n -> Exp m2 n
substTy r e = unTyExp (applyE r (TyExp e))

instance Subst Ty (TyExp n) where
    applyE :: forall m1 m2 n. Env Ty m1 m2 -> TyExp n m1 -> TyExp n m2
    applyE r (TyExp (EVar x)) = TyExp (EVar x)
    applyE r (TyExp (ELam ty b)) = 
        let q = substTy r (getBody b)
        in TyExp (ELam (applyE r ty) (bind q))
    applyE r (TyExp (EApp e1 e2)) = TyExp (EApp (substTy r e1) (substTy r e2))
    applyE r (TyExp (ETLam b)) = 
        let q = applyE (up r) (getBody b)
        in TyExp (ETLam (bind q))
    applyE r (TyExp (ETApp e1 t2)) = 
        TyExp (ETApp (substTy r e1) (applyE r t2))

-- | shift the type scope in the range of a term substiution
upTyScope :: Env (Exp m) n1 n2 -> Env (Exp (S m)) n1 n2
upTyScope = transform (substTy shift1E)
    
instance Subst (Exp m) (Exp m) where
    applyE :: forall m n1 n2. Env (Exp m) n1 n2 -> Exp m n1 -> Exp m n2
    applyE r (EVar x) = applyEnv r x
    applyE r (ELam ty b) = ELam ty (applyE r b)
    applyE r (EApp t1 t2) = EApp (applyE r t1) (applyE r t2)
    applyE r (ETLam b) =
        let (TyExp te) = getBody b 
        in ETLam (bind (TyExp (applyE (upTyScope r) te)))
    applyE r (ETApp e t) = ETApp (applyE r e) t    

-- System F context
data FCtx m n where
    Empty     :: FCtx Z Z
    ConsTmVar :: Ty m -> FCtx m n -> FCtx m (S n)
    ConsTyVar :: FCtx m n -> FCtx (S m) n

lookup :: Fin n -> FCtx m n -> Ty m
lookup FZ (ConsTmVar ty _) = ty
lookup FZ (ConsTyVar g) = applyE @Ty shift1E $ lookup FZ g
lookup (FS x) (ConsTmVar _ g) = lookup x g
lookup (FS x) (ConsTyVar g) = applyE @Ty shift1E $ lookup (FS x) g

tc :: FCtx m n -> Exp m n -> Maybe (Ty m)
tc g (EVar x) = return $ lookup x g
tc g (ELam ty b) = tc (ConsTmVar ty g) (getBody b)
tc g (EApp a b) = do 
    t1 <- tc g a
    t2 <- tc g b
    case t1 of 
        TArr t11 t12 -> if t1 == t2 then return t12 else Nothing
        _ -> Nothing
tc g (ETLam b) = do
    t1 <- tc (ConsTyVar g) (unTyExp (getBody b))
    return (TAll (bind t1))
tc g (ETApp a ty) = do 
    t1 <- tc g a 
    case t1 of 
        TAll tb -> return $ instantiate tb ty
        _ -> Nothing