[racket] metacircular interpreter, lexical scope, environment

From: Hendrik Boom (hendrik at topoi.pooq.com)
Date: Wed Dec 1 22:14:54 EST 2010

On Wed, Dec 01, 2010 at 09:09:04PM -0500, David Van Horn wrote:
> On 12/1/10 9:03 PM, YC wrote:
>> Thanks - I have read the substitution part of PLAI and thought of using
>> substitution as well, but it seems that substitution does not constitute
>> the full solution:
>>     * it only work with side-effect free code
> This is not entirely true, but substitution and effects are subtle.

The actual prblem is that the parameter is reevaluated every time it's 
used in teh called function.  THis lacke efficiency, but occasionally 
(as in Algol 60's call-by-name it's exactly what you need,)

>>     * it does not address closure
> You don't need closures in a substitution model.

No, as long as you take care to use systematic renaming of variables.

If you substiture A for x in (lambda y ...y.y.y...) and A contains a y, 
then you have to pick a brand new variable name (I'll call it z), 
replace y by z, and fo on substituting A for x in (lambra z ...z.z.z...) 
instead.  Doing this, you achieve the same effect as closures.  But 
closures can be a lot faster if done right.
>> Maybe I am looking too far ahead, but it seems like I cannot get away
>> from needing my own call stack?  Or am I missing something?
> I'm not exactly sure what you mean about your own call stack.  The  
> environment and the control stack are separate issues.

That's right.  You need your own environment.  Each funtion value 
contains the code to be executed, and the name-bindings for names that 
were around where the lambda-expression was evaluated that led 
to the function value.

You actually have quite some freedom as to how you go about evaluating 
the pambda-expression.  You can leave it mostly alone, and let the 
function value be a pair (lambda-body, environment-name-value-pairs), 
and then call the interpreter on the lambda-body after setting up an 
environment consiting of the functions environment and additional 
bindings for th parameters.

Or you can actually compile it to machine code at run-time.  But even 
that machine code will have to be able to find the bindings from the 
environment.  (it might even contain them).

Or you can precompile the functino body and essentially pass it the 
environment as an extra parameter.

Now it often happens, at least in many programming languages (Pascal is 
an example, but Scheme is not), that functions are imprisoned within the 
block they are declared in.  Once that block has been exited, it's 
impossible ever to call the function value.  In that case, and that case 
only, it's valid to put the environment on the stack.  THis is 
efficient, but only if the language has this function-lifetime 

-- hendrik

> David
> _________________________________________________
>  For list-related administrative tasks:
>  http://lists.racket-lang.org/listinfo/users

Posted on the users mailing list.