[gambit-list] Module System start up announcement

Taylor R Campbell campbell at mumble.net
Mon Mar 17 17:54:03 EDT 2008


   Date: Mon, 17 Mar 2008 21:16:51 +0000
   From: "David Rush" <kumoyuki at gmail.com>

   SYNTAX-CASE and explicit renaming (Larceny & S48, IIRC) are the main ones
   I'm aware of. Are there other major contenders?

Explicit renaming is a special case of syntactic closures.  I'm not
sure whether Andre van Tonder's macro system (one of them
high-numbered SRFIs) counts as another; in spirit it is explicit
renaming with a dynamically scoped renaming procedure in the macro
engine.

   > yet long experience has shown that this is almost
   > always a mistake;

   I never said anything different. But for the rare cases where hygiene
   breaking is desired, I find the semi-hygienic systems to be awfully cryptic.

In local macros that you plan to use in only one place to abbreviate
the construction of elaborate names, it is not necessary to concern
yourself with hygiene, and you can write a macro transformer procedure
with explicit renaming or syntactic closures that looks identical to
the DEFINE-MACRO version.  But for anything beyond purely local
macros, if you don't concern yourself with hygiene, however cryptic it
may be, you are bound to lose later on.

Here is an exercise for the reader to illustrate the problem.
Consider, for example, a DEFINE-ENUMERATION macro, defined so that

(define-enumeration foo (bar baz quux))

defines FOO to be a dispatching macro such that (FOO BAR) expands to
0, (FOO BAZ) to 1, and (FOO QUUX) to 2; and defines FOO-ENUMERANDS to
be the vector #(BAR BAZ QUUX).  Now suppose we write a SYNTAX-RULES
macro that uses DEFINE-ENUMERATION:

(define-syntax mumble-frotz
  (syntax-rules ()
    ((MUMBLE-FROTZ a b (c ...))
     (BEGIN ...
            (DEFINE-ENUMERATION AN-ENUMERATION (c ...))
            ...
            (FROB GROVEL AN-ENUMERATION-ENUMERANDS B))))).

Let's suppose DEFINE-ENUMERATION inserts the literal symbol
AN-ENUMERATION-ENUMERANDS into the output:

(define-syntax define-enumeration
  (er-macro-transformer
   (lambda (form rename compare)
     ...
       (,(rename 'DEFINE)
           ,(string->symbol
             (string-concatenate
              (map symbol->string
                   (list (name->symbol enumeration-name)
                         '-
                         'ENUMERANDS))))
         (,(rename 'QUOTE) ,(list->vector components)))
     ...))).

(We could even strip the renaming if you want, and make it look more
like the analogous DEFINE-MACRO.)  Why won't MUMBLE-FROTZ work?  Why
won't AN-ENUMERATION-ENUMERANDS in the output of MUMBLE-FROTZ refer to
the variable we want?

   And since I'm not interested in a religious advocacy war over hygiene, I'd
   like to know how you think module/namespace-based renaming is an adequate
   substitute for true hygiene in macros? Because I just don't see it.

I'm not interested in religious advocacy either; what I say here is a
purely technical judgement.

   Module-based renaming and macrotic renaming operate at sufficiently
   different levels of detail as to seem like rather different operators to me.

Hygienic renaming does not necessarily just mean affixing to each name
a tilde or octothorpe and a big or random number that we hope is
unique.  Hygienic renaming means associating with each name its
meaning, which may for local variables imply the affixing of
funny-looking suffixes, but for names whose meaning must persist
uniquely throughout a large system of components, it more likely
implies associating some description of the module from which the name
originated, or a path by which to get there.  There is no fundamental,
conceptual distinction between `module-based renaming' and `macrotic
renaming'.

Take, for example, a DELAY macro, in some module that exports FORCE
and DELAY and nothing else (perhaps throw EAGER and LAZY in to appease
those of us who actually want to use laziness).  We want to define the
DELAY macro in terms of an internal MAKE-PROMISE:

(define-syntax delay
  (syntax-rules ()
    ((DELAY expression)
     (MAKE-PROMISE (LAMBDA () expression))))).

But the relevant module does not export MAKE-PROMISE, so we can't just
insert the literal symbol MAKE-PROMISE into the output, even if the
user hasn't locally shadowed that name: if we insert the literal
symbol in DELAY's output, it would be just as if the user had written
MAKE-PROMISE in her own code, and she would encounter an undefined
variable error.

Hygiene guarantees that the *name* (not `symbol') in the output of
DELAY refers to exactly the same meaning as the symbol MAKE-PROMISE
meant in the environment where the DELAY macro was defined.  Exactly
how this name is represented is inessential to the point of hygiene.
It could be a qualified reference directly to the LAZINESS module, if
we have a global naming scheme for modules.  In Scheme48, it will be a
`generated' name, which remembers: the name used to invoke the macro
transformer that generated it, and the name by which the transformer
identified it.  Both names may be generated names themselves, but if
we follow them all the way to the end, by recursively looking up the
macro transformer, and looking up the name in that transformer's
environment, we shall eventually end up with the original meaning we
sought.

Whether this is the duty of the module system or of the macro system
doesn't really matter -- that's an implementation detail, and requires
only that the module system and the macro system cooperate.  That it
work is what matters.



More information about the Gambit-list mailing list