|
| ||
|
|
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 is:
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 becomes:
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 < dom >. Using the second version the type is different. It might be something that we call dom' defined as:
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? Var s
It works in both cases.
What about the third branch?
dom' will not work because in this case we would return:
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 < dom > definition and forget about dom' because it will not work. But we should all try to see if we can get it to work in a way or another (very unlikely).
So the final definition of staged eval should be:
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 ( \lambda) inside a bracket, then you generate a fresh variable name and use it for all bound occurrence of this bound variable.
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.