{-# LANGUAGE GADTs, ExistentialQuantification #-}

module Interp3 where

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

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

data Exp g t where 
  Var :: Var g (Value t) -> Exp g t
  Lit :: Int -> Exp g Int 
  Lam :: Exp (g, Value a) b -> Exp g (a -> b)
  App :: Exp g (a -> b) -> Exp g a -> Exp g b

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

data Value t where
  Const :: Int -> Value Int
  Clos  :: g -> Exp (g, Value a) b -> Value (a -> b)

instance Show (Value t) where
  show (Const i)  = show i
  show (Clos e t) = "Clos <env> " ++ show t ++ ")" 

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

interp :: g -> Exp g t -> Value t
interp e (Var x)     = sLookup x e
interp e (Lit i)     = Const i
interp e (Lam t)     = Clos e t
interp e (App t1 t2) =
	 case interp e t1 of 
	    Clos e' t1' -> interp (e', interp e t2) t1'


t0 = interp () (App (Lam (Var Z)) (Lit 2))

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

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



-- Homework: 

-- (1) Get rid of Value type. We don't need it. 
-- i.e. implement this interpreter.
-- interp' :: g -> Exp g t -> t

-- (2) given this datatype:

{-
data Ty =
  Int
| Arr Ty Ty

data UExp = 
  UVar Int
| ULit Int 
| ULam Ty UExp
| UApp UExp UExp 

-}

-- write a typechecker. i.e a function that takes a UExp and 
-- either fails or returns an Exp.