inductive Vector (α : Type u) : Nat → Type u | nil : Vector α 0 | cons : α → Vector α n → Vector α (n+1) infix:67 " :: " => Vector.cons inductive Ty where | int | bool | fn (a r : Ty) abbrev Ty.interp : Ty → Type | int => Int | bool => Bool | fn a r => a.interp → r.interp inductive HasType : Fin n → Vector Ty n → Ty → Type where | stop : HasType 0 (ty :: ctx) ty | pop : HasType k ctx ty → HasType k.succ (u :: ctx) ty inductive Expr : Vector Ty n → Ty → Type where | var : HasType i ctx ty → Expr ctx ty | val : Int → Expr ctx Ty.int | lam : Expr (a :: ctx) ty → Expr ctx (Ty.fn a ty) | app : Expr ctx (Ty.fn a ty) → Expr ctx a → Expr ctx ty | op : (a.interp → b.interp → c.interp) → Expr ctx a → Expr ctx b → Expr ctx c | ife : Expr ctx Ty.bool → Expr ctx a → Expr ctx a → Expr ctx a | delay : (Unit → Expr ctx a) → Expr ctx a inductive Env : Vector Ty n → Type where | nil : Env Vector.nil | cons : Ty.interp a → Env ctx → Env (a :: ctx) infix:67 " :: " => Env.cons def lookup : HasType i ctx ty → Env ctx → ty.interp | .stop, x :: xs => x | .pop k, x :: xs => lookup k xs def interp (env : Env ctx) : Expr ctx ty → ty.interp := fun e => match e with | .var i => lookup i env | .val x => x | .lam b => fun x => interp (x :: env) b | .app f a => interp env f (interp env a) | .op o x y => o (interp env x) (interp env y) | .ife c t e => if interp env c then interp env t else interp env e | .delay a => interp env (a ()) open Expr /- Examples -/ def add : Expr ctx (Ty.fn Ty.int (Ty.fn Ty.int Ty.int)) := lam (lam (op (.+.) (var .stop) (var (.pop .stop)))) #guard interp Env.nil add 10 20 == 30 def fact : Expr ctx (Ty.fn Ty.int Ty.int) := lam (ife (op (.==.) (var .stop) (val 0)) (val 1) (op (.*.) (delay fun _ => app fact (op (.-.) (var .stop) (val 1))) (var .stop))) decreasing_by sorry #guard interp Env.nil fact 10 == 3628800