I like Gambit's namespace directive; beauty comes in simplicity. I know the Scheme purist guy throws up a little in his mouth whenever he thinks about it, and here's why: it uses name mangling. It seems that there are two types of Scheme programmers; those that care more about practical applications of using Scheme, and those that care more about being theoretically correct. It's rather non-deterministic which one is better. Often the purist comes out in the end when his app behaves the way it should, but many times the practical guy wins when he gets things done quick or his app runs faster.
In general, I love how dedicated the Scheme community is with being algorithmically correct. I think we should almost always favor theoretically-correct design over hacked-for-optimization design. Almost, though, because there are cases where we want to apply the greatness of Scheme to areas which require high performance, such as graphics engines. And this is where I am (designing a graphics engine), so, however imperfect it may seem, I will tend to hack the design of my scheme code to favor critical performance.
On a quick side note, it seems that Gambit represents a system which strives to be theoretically correct, but recognizes the "need for speed." Scheme48, on the other hand, will always make decisions purely based on the formal theory behind it.
Back to Gambit's module system. The namespace directive simply rewrites definitions with the supplied prefix. This maps well to Gambit's builtin define-type, which is a basic data type definition. define-type is a macro which writes out several functions which make the object, set/get its properties, etc. Using the namespace directive, these will get properly renamed.
Well, kind of. You don't exactly import and export symbols, but you simply pass a list of symbols along with the namespace declaration which claim "this namespace owns these symbols." Any reference to those variables will refer to (namespace)#(variable). If you don't pass any variables, you are claiming your namespace owns everything, which is why you lose all the standard definitions:
> (namespace ("foo#"))Most of the time people explicitly list the variables they want associated with the namespace:
> (define a 5)
*** ERROR IN (console)@2.2 -- Unbound variable: foo#define
> (namespace ("foo#" a))This presents a problem though: for rewriting macros such as define-type we don't always know the real symbols to export (even if we do, it would be tedious to list them, that's the whole point of the macro). Are these the kind of problems syntax-case solves? I've heard that a proper module system works with syntax-case and this kind of thing can be done.
> (define a 5)
> (define b 7)
> a
5
> foo#a
5
> foo#b
*** ERROR IN (console)@7.1 -- Unbound variable: foo#b
I want to stick with name mangling and something similar to define-type however because of the performance gain in Gambit with top-level functions. I'm not sure what to do about exporting types (aka objects), I'll have to prototype and see how much I need to use types outside of modules. I could probably get by with defining constructor/getter/setter functions that should be used by the outside world, and manually export them. That may be better anyway; only let the local namespace modify the type data directly.
Of course, I could write a tool that analyzes the code and generating a proper namespace header file. I'll have to see if that's necessary though.
We can still get macros like define-type to bind the created function to the local namespace by doing something like this:
> (namespace ("graphics#"))Another solution might be to always use fully qualified names. I'll have to take a look at Christian Jaeger's chjmodule system to see how he handles some of this. I may look into using his system in the future.
> (##include "~~/lib/gambit#.scm")
> (define-type point x y z)
> (namespace (""))
> (make-point 1 2 3)
*** ERROR IN (console)@5.2 -- Unbound variable: make-point
1> ,d
> (graphics#make-point 1 2 3)
> #
Although it seems somewhat primitive, I like the simplicity that Gambit offers. It seems that exporting special types such as objects is an inherent problem with any module system, where special support must be built. I'd rather write that support so that I can use objects that way I want to.