(* Real evaluators don't rewrite function bodies eagery, replacing the argument variable with its value right away. Instead, they use an environment to look up variables as needed. *) type xvar = string type xval = Num of int | Fun of (xval -> xval) type xpr = Const of int (* Now, all source values are integers *) | Minus of xpr * xpr | Times of xpr * xpr | Lam of xvar * xpr | Var of xvar | App of xpr * xpr | IfZero of xpr * xpr * xpr (* New structure for environments *) type xenv = Empty | Extend of xvar * xval * xenv ;; (* Revised examples for modified `Const' encoding *) let five = Const(5) let protofac = Lam("f", Lam("n", IfZero(Var("n"), Const(1), Times(Var("n"), App(App(Var("f"), Var("f")), Minus(Var("n"), Const(1))))))) let fac = App(protofac, protofac) let onetwenty = App(fac, five) ;; (* The new evaluator. `replace' has been replaced by `lookup'. *) let rec eval = function (Const(v), e) -> Num(v) | (Minus(m1, m2), e) -> let Num(n1) = eval(m1, e) and Num(n2) = eval(m2, e) in Num(n1 - n2) | (Times(m1, m2), e) -> let Num(n1) = eval(m1, e) and Num(n2) = eval(m2, e) in Num(n1 * n2) | (Lam(var, m), e) -> Fun(fun v -> eval(m, Extend(var, v, e))) | (App(m1, m2), e) -> let Fun(f) = eval(m1, e) in f(eval(m2, e)) | (IfZero(m1, m2, m3), e) -> let Num(n) = eval(m1, e) in eval((if (n==0) then m2 else m3), e) | (Var(var), e) -> lookup(var, e) and lookup = fun (var, Extend(var2, v, e)) -> if (var = var2) then v else lookup(var, e) ;; eval(onetwenty, Empty) ;;