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

-- Homework assignment: complete this interpreter

module Interp where

-- No scoping errors for this interpreter
-- No typing errors 
-- Uses type families

-- Tagless interpreter that uses a first-order rep 
-- of function values (i.e. closures) instead of 
-- higher-order rep.

-- type of interp is Env g -> Exp g t -> Result t
-- where the result of a function type is a closure.

type family Result t 
type instance Result Int = Int
type instance Result (a -> b) = Closure a b


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

-- An environment datatype so that we can ensure that 
-- we can print out closures.  (i.e. each value added 
-- to the Env must be Showable.)

data Env g where
  ENil  :: Env ()
  ECons :: Show (Result t) => Env g -> Result t -> Env (g,t) 

-- A closure is an environment paired with a term w/ 1 free variable.
data Closure a b where
  Clos  :: Show (Result a) => Env g -> Exp (g, a) b -> Closure a b

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

---------------------
-- Show instances

instance (Show (Var g t)) where
  show Z = "Z"
  show (S x) = "S " ++ show x

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

instance Show (Closure a b) where
  show (Clos e t) = "(Clos " ++ show e ++ "**" ++ show t ++ ")" 

instance Show (Env g) where
  show ENil = "()"
  show (ECons g r)  = "(" ++ show g ++ "," ++ show r ++  ")"

---------------------
-- Interpreter
interp :: Show (Result t) => Env g -> Exp g t -> Result t
interp = undefined
{- Fill this part in -}


-------------------------
-- examples
e0 :: Exp () (Int -> Int)
e0 = Lam (Var Z) 

t0 = interp ENil (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))

e1 :: Exp ((), Int) ((Int -> Int) -> Int)
e1 = Lam (App (Var Z) (Var (S Z)))

e2 :: Exp () (Int -> (Int -> Int) -> Int)
e2 = Lam e1

t3 = interp ENil (App e2 (Lit 3))

-- Once you have a Closure, what do you do with it?
apply :: Show (Result b) => Closure a b -> Result a -> Result b
apply (Clos e t) ra = interp (ECons e ra) t

