|
|
||
|
|
Start of topic | Skip to actions
Multi-stagingUnstaged untyped LC interpreterRecall that a simple interpreter for the untyped lambda calculus should look like this:type dom = | VInt of int | VFun of dom -> dom type exp = | Var of string | App of exp * exp | Lam of string * exp | Int of int let envZero x = raise NotFound let ext env x v = \lambda y. if x = y then v else env x let unVFun v = match v with |VFun f -> f |_ -> raise Error let rec eval e env = match e with | Int i -> VInt i | Var s -> env s | Lam(x,e) -> VFun (\lambda v. eval e (ext env x v)) | App(e1,e2) -> (unVFun (eval e1 env))(eval e2 env) And the type of eval: exp -> (string -> dom) -> dom Staging the LC interpreterThis interpreter's performance is the not the bet attainable because we have 2 overheads:
let rec eval e env = match e with | Int i -> VInt < i > | Var s -> env s | Lam(x,e) -> < VFun (\lambda v. ~(eval e (ext env x < v >))) > | App(e1,e2) -> < (unVFun ~(eval e1 env)) ~(eval e2 env) > and the type of eval: exp -> (string -> < dom >) -> < dom > Is this correct? No. Let's look at the first branch. The question is do we want: | Int i -> < VInt i >or | Int i -> VInt < i > The second is better because we delay the evaluation of less things, but will this type check? Actually using the first version, the return type of this branch is type dom' = |VInt of <int> |VFun of <dom> -> <dom>This will change the type of eval to:
eval: exp -> (string -> dom') -> dom' What about the second branch? It works in both cases. What about the third branch?
VFun < (\lambda v. ~(eval e (ext env x < v >))) >and note that (eval e (ext env x < v >)) has type dom' which can't be passed to a ~.
Therefore we have to go back to the So the final definition of staged let rec eval e env = match e with | Int i -> < VInt i > | Var s -> env s | Lam(x,e) -> < VFun (\lambda v. ~(eval e (ext env x < v >))) > | App(e1,e2) -> < (unVFun ~(eval e1 env)) ~(eval e2 env) > Having type: eval: exp -> (string -> < dom >) -> < dom > ExampleWhat do we get if we run the following?
eval(parse "\lambda x.x") envZero
= eval (Lam ("x", Var "x")) envZero
= < VFun (\lambda v.v) >
eval(parse "\lambda x. \lambda y. x") envZero
= eval (Lam ("x", (Lam ("y", Var "x")))) envZero
= < VFun (\lambda v. VFun (\lambda v. v)) >
The problem here is that we get a wrong answer the correct answer should be: < VFun (\lambda v1. VFun (\lambda v2. v1)) > And that's what happens in MetaOCaml, and that's called hygienic renaming. This occurs as follows: Every time you see a binder ( HomeworkStage your tagless interpreters.
Topic Actions: Edit | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r5 < r4 < r3 < r2 < r1 | More topic actions
Webs: Main | TWiki | Africa | EmbeddedSystems | Gpce | Houston | International | K12 | MetaOCaml | MulticoreOCR | ProgrammingLanguages | RAP | RIDL | Sandbox | SpeechClub | Teaching | Texbot | WG211 Web Actions: |
|
This work is licensed under a Creative Commons Attribution 2.5 License. Please follow our citation guidelines.