On Mar 26, 11:55 pm, Abdulaziz Ghuloum <[email protected]> wrote: > Here, you're talking about the visibility/scope/meaning of > identifiers (those that are imported and those that are > defined with define/define-syntax). The term "phase > separation" is ambiguous as it pertains to other issues like > when, why, and how many times libraries are expanded/invoked.
Ok, I was indeed unsure about the terminology. > > 1. full phase separation (mzscheme, Larceny); there is an > > infinite tower of metalevels, and you are forced to specify > > at which phase you want to import the names; > > The number of meta levels is always bounded by the number of > things you type in the import-for clause. It's always finite > in the mzscheme/larceny world. In Ikarus however, imported > identifiers are available at all levels. Yes, I should have written "potentially infinite" in the sense that you can nest macros arbitrarily deep and in principle you can reach any level. > > 2. partial phase separation (Ikarus); importing a name > > imports it at all levels; > > > 3. no phase separation (REPL of Ikarus and Ypsilon). > > > The rationale for 1 is that some people (not me) want the > > freedom to use different languages at different levels. > > The rationale for 1 is in Matthew Flatt's "Composable and > Compilable Macros" paper. I don't recall there is any mention > of "different languages at different levels" in that paper. > The main rationale that I get is to control side effects that > happen at compile time (see the zoo examples). I was not convinced by that example, but now I have no time to discuss it further. I was referring to Eli Barzilay who said many times explicitly that he wanted the ability to use different languages at different levels and this is used in PLT Scheme. I am not convinced by Eli's point either. > Nop! The compiler does NOT have access to helper functions > defined in the same compilation unit until the entire unit is > expanded. Consider a library containing: > > (define t (make-table ---)) > (define-syntax m (lambda (x) --- use t ---)) > (initialize-table! t) > > At the time the code for m is expanded, the expander has not > yet finished expanding the library, and has no knowledge of > the initialization code. If we allow the use of t, we'd be > hosed for using an uninitialized table. Yes, than we would need to write (define t (make-table ---)) (initialize-table! t) (define-syntax m (lambda (x) --- use t ---)) and then people would complain, because when changing the table at runtime they would see no effect in the macro. I thought about this, I am not sure if one should put restrictions and accept only definitions involving immutable objects (and therefore the same both at compile-time and run-time). Another issue would be the following: (define today (current-date)) (define-syntax some-macro (lambda (x) --- use today ---) (define some-function (lambda (x) --- use today --) When using some-macro we would get the compilation date (possible months ago) and when using some-function we would get the date of execution. However, this happens even in current Scheme, if you define a macro depending on the current date. So, I realize evaluating names both and runtime and at compile time could give to surprising results and people should be warned about the traps and pittfalls. > > Instead, R6RS-compatibility forces us to put the auxiliary > > functions in an auxiliary module and to import them. > > No. What forces us is the desire to be able to fully expand > and compile each library without having to also evaluate it. I am trying to understand all the reasons why we do not want to evaluate each library. Is there more than the effects we discussed before? For instance a (define something (read)) when evalued at compile-time would stop the compiler, read an expression from stdin and continue; when evalued at run-time it could read a completely different expression, so it would be a foolish idea for sure.
