module WellTyped

-- Idris2's "Well Typed Interpreter" example
-- This is a little painful for lack of overloading.
-- I can add idris/haskell style sections.
-- This is revealing some issues. :/

-- I had to put {0 ctxt : Vect n Ty} →  in manually, the `∀ ctxt` wasn't inferring the type.
-- This may be because we have `n`, which becomes a parameter when do it manually.
-- That would explain why Idris adds those...

-- This also revealed an issue in case tree building where a bound name was accidentally made into a defined one.

import Prelude
import Data.Vect
import Data.Fin

data Ty = TyInt | TyBool | TyFun Ty Ty

interpTy : Ty → U
interpTy TyInt = Int
interpTy TyBool = Bool
interpTy (TyFun x y) = interpTy x → interpTy y

data HasType : ∀ n. (i : Fin n) → Vect n Ty → Ty → U where
  Stop : ∀ t n. {0 ctxt : Vect n Ty} → HasType FZ (t :- ctxt) t
  Pop  : ∀ n t u. {0 ctxt : Vect n Ty} {0 k : Fin n} → HasType k ctxt t → HasType (FS k) (u :- ctxt) t

data Expr : ∀ n. Vect n Ty → Ty → U where
  Var : ∀ t n. {0 ctxt : Vect n Ty} {0 i : Fin n} → HasType i ctxt t → Expr ctxt t
  Val : ∀ n. {0 ctxt : Vect n Ty} → (x : Int) → Expr ctxt TyInt
  Lam : ∀ a n t. {0 ctxt : Vect n Ty} → Expr (a :- ctxt) t → Expr ctxt (TyFun a t)
  App : ∀ n a t. {0 ctxt : Vect n Ty} → Expr ctxt (TyFun a t) → Expr ctxt a → Expr ctxt t
  Op  : ∀ a b c n. {0 ctxt : Vect n Ty} → (interpTy a → interpTy b → interpTy c) →
        Expr ctxt a → Expr ctxt b → Expr ctxt c
  -- TODO Idris has Lazy on the arms, should work if we explicitly force
  If  : ∀ n a. {0 ctxt : Vect n Ty} → Expr ctxt TyBool →
        (Expr ctxt a) →
        (Expr ctxt a) → Expr ctxt a

infixr 4 _:::_
data Env : ∀ n. Vect n Ty → U where
  Empty  : Env VNil
  _:::_ : ∀ a n. {0 ctxt : Vect n Ty} → interpTy a → Env ctxt → Env (a :- ctxt)

lookup : ∀ n t. {0 ctxt : Vect n Ty} {0 i : Fin n} → HasType i ctxt t → Env ctxt → interpTy t
lookup Stop (x ::: xs) = x
lookup () Empty
lookup (Pop k) (x ::: xs) = lookup k xs

interp : ∀ n t. {0 ctxt : Vect n Ty} → Env ctxt → Expr ctxt t → interpTy t
interp env (Var i)    = lookup i env
interp env (Val x)    = x
interp env (Lam x)    = \y => interp (y ::: env) x
interp env (App x y)  = interp env x $ interp env y
interp env (Op f x y) = f (interp env x) (interp env y)
interp env (If x y z) = if interp env x then interp env y
                                        else interp env z

add : ∀ n. {0 ctxt : Vect n Ty} → Expr ctxt (TyFun TyInt (TyFun TyInt TyInt))
add = Lam (Lam (Op _+_ (Var Stop) (Var (Pop Stop))))

fact : ∀ n. {0 ctxt : Vect n Ty} → Expr ctxt (TyFun TyInt TyInt)
-- TODO the `Bool =?= interpTy ...` is stuck on ?m14
-- I think we may need to need to delay the constraint until m14 is solved?
-- But I think I have code to do that already
fact = Lam (If (Op {_} {_} {TyBool} _==_ (Var Stop) (Val 0))
               (Val 1)
               (Op _*_ (App fact (Op _-_ (Var Stop) (Val 1)))
                       (Var Stop)))

-- main : IO Unit
-- main = do putStr "Enter a number: "
--           Right x <- readLine | _ => putStrLn "EOF"
--           printLn (interp Empty fact (stringToInt x))
