Marc Feeley wrote:
So my conclusion is that you don't like "define" = "set!" + allocation of global var if it doesn't exist. Right?
Thanks for the clarification. I like those semantics. The "global var" part becomes confusing when dealing with a module system. As you've said, it's really an issue with how to resolve identifiers.
Your example:
(##module m1 gambit (export foo)) (define (foo) (print "m1#foo\n"))
(##module m2 gambit) (##use m1) (define (bar) (foo)) ;; refers to which foo? ;... lots of definitions ... (define (foo) (print "m2#foo\n"))
That is right, that is ambiguous. I agree with Per that we should stay within our current module, always. I think the easiest and most efficient way of doing this, and if we want to keep the "define" = "set!" semantics, is to import all of the identifiers of your imported packages into your local environment (which is what chjmodule does I think). Gambit's builtin procedures would also be imported locally for every module.
So this would be the expanded source of your example above:
(##module m1 gambit (export foo)) (define (m1#print ##print) (define (m1#foo) (m1#print "m1#foo\n"))
(##module m2 gambit) (###load m1) ;; <- (##use m1) is expanded (define m2#foo m1#foo) ;; to both these lines
(define (m2#bar) (m2#foo)) ;... lots of definitions ... (define (m2#foo) (print "m2#foo\n"))
Now it would handle that situation the same way Gambit currently does with the global environment. This creates module-level scoping of all identifiers for each module. Note that the above example doesn't include all the primitive forms that would be imported, it would really look more like this (the ## prefix will be replaced whenever you modularize Gambit):
(##module m1 gambit (export foo)) (define m1#car ##car) (define m1#cdr ##cdr) (define m1#map ##map) (define m1#vector ##vector) ... (all of Gambit's builtins) ...
Now the global environment is still a problem. How do we reference the global environment in a module? I say don't let it, or keep the ## syntax.
The REPL is also interesting too. It's another kind of environment. I like the way Scheme48 does it: provide a set of builtin commands (Scheme48's "configuration language") which allow you to open, go inside, reload, etc. modules (akin to what Per did). When evaluating commands, it evaluates them in the environment of the current module. If you are not inside a module, you are inside an implicit "REPL" module, *not* the global environment.
You could effectively do away with the global environment. I don't see any use for it as long as you provide the REPL with a place to go when it's not in a module. We can make it backwards compatible with a few hacks.
Per, I don't know how your system resolves symbols. Fixing the DEFINE problem is one thing, but how do you resolve other identifiers?
The main question in my mind... what is lacking in this module system to implement other module systems (such as Scheme48's, PLT's, Chicken's, Bigloo's, Guile's). I would like people on this list (yes *you*) to chip in by picking one of these module systems and trying to implement it on top of strawman
Unfortunately I don't have the time to give you as full of a report as I'd like, but off the top of my head:
* Scoping correctness (ability to define "car" locally, for example) * Identifier renaming * Syntactic tower-like behavior (being able to import a module for macro expansion time)
Per wrote:
Incidentally, this is very similar to how black hole started its life: I wanted something quick that just worked, to be able to organize code. I have since then learned the hard way which features a module system has to have to be practical to use. I quickly realized that I wanted to support define-type records. So I hacked together something for that. After fixing all the bugs, I basically had a very mundane macro expansion function. Later on I realized that hygiene is a very good thing to have. And so on.
From personal experience, I can tell that the approach you are taking will require lots of extra work.
I have to agree with Per. It feels a bit overwhelming right now to support all kinds of different module system. We should figure out exactly what Gambit needs to just have basic module semantics, and implement them in a way that fits Gambit. Then we can talk about adding features or tweaking the module system to support other ones.
- James