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

Reply via email to