On 13 Oct 2009, at 7:52 am, Brian Harvey wrote: > Of course what I really should have said is that /I/ have trouble > understanding this multi-phase view of Scheme programs, and so in > sheer > self-interest I have to beg that future Schemes (at least WG1 > Schemes) not be > designed in a way that I'm not smart enough to use.
I think the phases concept is a little broken, too. But I'm pretty sure you can have low-level macros without it, to boot. I know that it seems like CL makes do without, but they still lurk under the covers in EVAL-WHEN, in a messier and more restrictive form. If you have a module that defines: a) A bunch of procedures for, say, matrix algebra b) A bunch of macros that do things with matrices that do some matrix algebra at macro-expansion time, and generate code that does more matrix algebra at run time ...I see no real reason why the Scheme compiler/interpreter/REPL can't figure that out itself. The cases that make it awkward - such as when set! crops up in macro-expansions - can be identified and guarded against by making the language specification warn that their semantics are undefined in these contexts, and if it's done right, nobody need shed a tear. Now, we know this kind of behaviour can be provided by a REPL. For it, macro-expansion time and run-time run together; the procedures are defined, and the macros are defined, and so when the macros are actually used the procedures are available for macro-expansion, then immediately for executing the resulting code. It's more complex for compilers - but not if they're implemented more creatively! The process of compilation can be cast in terms of a REPL, for a start. Imagine, if you will, an image-based system where you deploy your app by loading it into a REPL then freezing the image. As it's a REPL, the above logic will apply, and the resulting image will contain code that was macro-expanded in an environment with those procedures defined, and that environment lives on until run time; even if the macro-expander does some set!-ing, then the final value of that mutable cell after all the macros are expanded will be frozen and survive into run time (which is great, AIUI, for implementing generic function dispatchers!) But compilation into native code ELF executables can be considered as a form of image-freezing, too. Even a Scheme->C compiler like Chicken can work this way. It could run the body of every loaded module through a REPL to create a namespace (implementing 'lambda' as a a naive interpreter would, just storing the source code form of the procedure *after* macro expansion, and the closed-over environment), then wrap up all non-definition forms in the top-level with (lambda () ... ) and do the same. In fact, ideally, one would rewrite the top- level into that big letrec* form, with a lambda in the body containing the run-time actions of the program. The end result will be that the top level 'evaluates' to a thunk that "runs the program". One can then 'compile' this by code-walking it and generating C, or native code, or bytecode, or whatever you want. Any mutable cells referred to are turned into variables in the C code that start off with the value found in those cells during compilation. We're allowed to puke if we find non-persistable things like file handles lurking therein, but we handle closures by compiling them. Now, I've made certain assumptions about order of evaluation there; top-level expressions in the bodies of modules are executed at compile- time, and in the top-level program, expressions within definitions are executed at compile-time, but the top-level expressions in the top- level program are executed at run time (otherwise, there's nothing *left* to execute at run time). It'd be nice if the Scheme standard defined what-runs-when to such a level of vagueness as to let an implementer do that, while still letting programmers get stuff done without distress. It'd perhaps be even nicer if the Scheme standard provided a top-level-program abstraction that made it explicit what should be the run-time versus compile-time behaviour, so programmers aren't forced to ship compile-time stuff into module top-levels. I'm thinking of cases like set!-ing a global that affects the behaviour of macro-expansion (or wrapping a bunch of definitions in a parameterize, which might be nicer). But anyway. When I look at things like phase distinctions, to let a compiler figure out how to get to a definition that's RIGHT THERE IN FRONT OF IT, I wince rather. IMHO, compilers of metaprogramming- capable languages should, first and foremost, be interpreters for that language, so that it can interpret the code that metaprograms its way to producing the final program... which can then be compiled, once it's been generated and is all static and free of macroexpansions. ABS -- Alaric Snell-Pym Work: http://www.snell-systems.co.uk/ Play: http://www.snell-pym.org.uk/alaric/ Blog: http://www.snell-pym.org.uk/archives/author/alaric/ _______________________________________________ r6rs-discuss mailing list [email protected] http://lists.r6rs.org/cgi-bin/mailman/listinfo/r6rs-discuss
