On 2-Apr-09, at 12:23 PM, James Long wrote:
On Thu, Apr 2, 2009 at 8:33 AM, Marc Feeley feeley@iro.umontreal.ca wrote:
Note that A requires B both for run time and expansion time. In a model with a syntactic tower there are two instances of B, that is B0 (run time) and B1 (expansion time). What is the relationship between v from B0 and v from B1 (let's call them B0.v and B1.v)? I feel that there should be no interaction between levels, except the macro expander acts as a bridge between levels. So B0.v and B1.v should be distinct variables.
Do we all agree on this?
Yep, I agree.
There will be a substantial run time cost to implement this because each instance of a module is basically a closure, and thus there will be an additional indirection to access global variables.
I'm assuming you mean "run-time" of the compiler.
No I mean run time of the program.
You could, of course, compile all this out. But this might make the compilation stage take longer.
If you wanted to keep all the symbols in a flat list for efficiency, what about mangling names of identifiers in syntactic environments? It seems that you would know which level of the syntactic tower you are at when you evaluate a piece of code. You could suffix the level to an identifier, so B1.v would become B1.v-1.
To compile it out you'd need the compiler to generate an object file (or .c/.o file) for every possible value of i. So module B.scm would generate B0.o1, B1.o1, B2.o1, etc (obviously in a lazy way for above a certain level) and the system would load the one appropriate for a particular level. However this would be very wasteful since the code would be duplicated. This is akin to implementing closures like this:
;;; source code (define (f x) (lambda (y) (+ y (* x y)))) (define a (f 11)) (define b (f 22)) (define c (f 33))
;;; generated code (define (f x) (lambda (y) (+ y (* x y)))) (define a (lambda (y) (+ y (* 11 y)))) (define b (lambda (y) (+ y (* 22 y)))) (define c (lambda (y) (+ y (* 33 y))))
Of course in the case of modules the "code" part of the "closure" is much larger than in the above example.
So a more space efficient approach is to add to each functional object (closure, primitive or return address) a pointer to the "global environment" of that module. When a module is "loaded" (in other words instantiated) a new global environment would be allocated, and all of its functional objects would be created and setup with a pointer to that instance's global environment. At run time an additional indirection would be required to get to the content of a module's global variables. Moreover, there is an overhead when calling functions because the "module environment" of the target function has to be read.
Marc