
{-# LANGUAGE GADTs, ExistentialQuantification, TypeFamilies, EmptyDataDecls #-}

module Interp3 where

-- No scoping errors for this interpreter
-- No typing errors 

-- An example interpreter that uses "object"-language 
-- indices instead of Haskell-types as indices

-- "datatype" for the types of the object language
data TInt = TInt
data TArr a b = TArr a b
  
-- type interpretation. Mapping between object
-- language types and Haskell types
type family I a 
type instance I TInt = Int
type instance I (TArr t1 t2) = I t1 -> I t2 

data Var g t where
   Z :: Var (g, t) t
   S :: Var g t -> Var (g,t') t

-- The environment contains Haskell values, so need to 
-- interpret their types.
data Exp g t where 
    Var :: Var g (I t) -> Exp g t
    Lit :: Int ->  Exp g TInt
    Lam :: t1 -> (Exp (g, I t1) t2) -> Exp g (TArr t1 t2)
    App :: Exp g (TArr t1 t2) -> Exp g t1 -> Exp g t2

instance Show (Exp g n) where
  show (Var lt)      = "Var"
  show (Lit i)       = "(Lit " ++ show i ++ ")"
  show (Lam t' t)    = "(Lam " ++ show t ++ ")"
  show (App t u)     = "(App " ++ show t ++ " " ++ show u ++ ")"

sLookup :: Var env t -> env -> t
sLookup Z (g,v)      = v
sLookup (S x) (g, v) = sLookup x g

interp :: env -> Exp env t ->  I t
interp e (Var x)     = sLookup x e
interp e (Lit i)     = i
interp e (Lam t' t)  = \x -> interp (e,x) t
interp e (App t1 t2) = (interp e t1) (interp e t2)

-- need this type annotation to make it work.
e0 :: Exp () (TArr TInt TInt)
e0 = Lam TInt (Var Z)
t0 = interp () (App e0 (Lit 2))

-- doesn't type check
-- t1 = interp nil (Var (s z))

-- doesn't type check
-- t2 = interp () (App (Lit 2) (Lit 3))

