System features
===============

Scheme48 provides a variety of miscellaneous features built-in to the system.

Miscellaneous features
----------------------

The structure features provides some very miscellaneous features in Scheme48.

 -- procedure: immutable? object returns boolean
 -- procedure: make-immutable! object returns object
     All Scheme objects in Scheme48 have a flag determining whether or not they may be mutated.  All immediate Scheme objects ((), #f, etc) are immutable; all fixnums (small integers) are immutable; and all stored objects --- vectors, pairs, etc --- may be mutable.  Immutable? returns #t if OBJECT may not be mutated, and make-immutable!, a bit ironically, modifies OBJECT so that it may not be mutated, if it was not already immutable, and returns it.

          (immutable? #t)                         => #t
          (define p (cons 1 2))
          (immutable? p)                          => #f
          (car p)                                 => 1
          (set-car! p 5)
          (car p)                                 => 5
          (define q (make-immutable! p))
          (eq? p q)                               => #t
          (car p)                                 => 5
          (immutable? q)                          => #t
          (set-car! p 6)                          error--> immutable pair lisp

 -- procedure: string-hash string returns integer-hash-code
     Computes a basic but fast hash of STRING.

          (string-hash "Hello, world!")           => 1161 lisp

 -- procedure: force-output port returns unspecified
     Forces all buffered output to be sent out of PORT.

     This is identical to the binding of the same name exported by the Ports, i/o structure.

 -- procedure: current-noise-port returns output-port
     The current noise port is a port for sending noise messages that are inessential to the operation of a program.

The silly structure exports a single procedure, implemented as a VM primitive for the silly reason of efficiency, hence the name of the structure. (The author of this manual is not at fault for this nomenclature.)  It is used in an inner loop of the reader.

 -- procedure: reverse-list->string char-list count returns string
     Returns a string of the first COUNT characters in CHAR-LIST, in reverse.  It is a serious error if CHAR-LIST is not a list whose length is at least COUNT; the error is not detected by the VM, so bogus pointers may be involved as a result. Use this routine with care in inner loops.

The debug-messages structure exports a procedure for emitting very basic debugging messages for low-level problems.

 -- procedure: debug-message item ... returns unspecified
     Prints ITEM ... directly to an error port, (On Unix, this is stderr, the standard I/O error output file.) eliding buffering and thread synchronization on the Scheme side. Objects are printed as follows:

       * Fixnums (small integers) are written in decimal.

       * Characters are written literally with a #\ prefix.  No naming translation is performed, so the space and newline characters are written literally, not as #\space or #\newline.

       * Records are written as #{TYPE-NAME}, where TYPE-NAME is the name of the record's type.

       * Strings and symbols are written literally.

       * Booleans and the empty list are written normally, ie as #t, #f, or ().

       * Pairs are written as (...).

       * Vectors are written as #(...).

       * Objects of certain primitive types are written as #{TYPE}: procedures, templates, locations, code (byte) vectors, and continuations. (Continuations here are in the sense of VM stack frames, not escape procedures as obtained using call-with-current-continuation.)

       * Everything else is printed as #{???}.

The code-quote structure exports a variant of quote that is useful in some sophisticated macros.

 -- special form: code-quote object returns object
     Evaluates to the literal value of OBJECT.  This is semantically identical to quote, but OBJECT may be anything, and the compiler will not signal any warnings regarding its value, while such warnings would be signalled for quote expressions that do not wrap readable S-expressions: arbitrary, compound, unreadable data may be stored in code-quote.  Values computed at compile-time may thus be transmitted to run-time code.  However, care should be taken in doing this.

Various utilities
-----------------

The util structure contains some miscellaneous utility routines extensively used internally in the run-time system.  While they are not meant to compose a comprehensive library (such as, for example, [SRFI 1]), they were found useful in building the run-time system without introducing massive libraries into the core of the system.

 -- procedure: unspecific returns unspecific
     Returns Scheme48's unspecific token, which is used wherever R5RS uses the term `unspecific' or `unspecified.'  In this manual, the term `unspecified' is used to mean that the values returned by a particular procedure are not specified and may be anything, including a varying number of values, whereas `unspecific' refers to Scheme48's specific `unspecific' value that the unspecific procedure returns.

 -- procedure: reduce kons knil list returns final-knil
     Reduces LIST by repeatedly applying KONS to elements of LIST and the current KNIL value.  This is the fundamental list recursion operator.

          (reduce KONS KNIL
                  (cons ELT1
                        (cons ELT2
                              (...(cons ELTN '())...))))
              ==
          (KONS ELT1
                (KONS ELT2
                      (...(KONS ELTN KNIL)...))) lisp

          Example:

               (reduce append '() '((1 2 3) (4 5 6) (7 8 9)))
                   => (1 2 3 4 5 6 7 8 9)
               (append '(1 2 3)
                       (append '(4 5 6)
                               (append '(7 8 9) '())))
                   => (1 2 3 4 5 6 7 8 9) lisp

 -- procedure: fold combiner list accumulator returns final-accumulator
     Folds LIST into an accumulator by repeatedly combining each element into an accumulator with COMBINER.  This is the fundamental list iteration operator.

          (fold COMBINER
                (list ELT1 ELT2 ... ELTN)
                ACCUMULATOR)
              ==
          (let* ((accum1 (COMBINER ELT1 ACCUMULATOR))
                 (accum2 (COMBINER ELT2 accum1))
                 ...
                 (accumN (COMBINER ELTN accumN-1)))
            accumN) lisp

          Example:

               (fold cons '() '(a b c d))
                   => (d c b a)
               (cons 'd (cons 'c (cons 'b (cons 'a '()))))
                   => (d c b a) lisp

 -- procedure: fold->2 combiner list accumulator1 accumulator2 returns [final-accumulator1 final-accumulator2]
 -- procedure: fold->3 combiner list accumulator1 accumulator2 accumulator3 returns [final-accumulator1 final-accumulator2 final-accumulator3]
     Variants of fold for two and three accumulators, respectively.

          ;;; Partition LIST by elements that satisfy PRED? and those
          ;;; that do not.

          (fold->2 (lambda (elt satisfied unsatisfied)
                     (if (PRED? elt)
                         (values (cons elt satisfied) unsatisfied)
                         (values satisfied (cons elt unsatisfied))))
                   LIST
                   '() '()) lisp

 -- procedure: filter predicate list returns filtered-list
     Returns a list of all elements in LIST that satisfy PREDICATE.

          (filter odd? '(3 1 4 1 5 9 2 6 5 3 5))
              => (3 1 1 5 9 5 3 5) lisp

 -- procedure: posq object list returns integer or #f
 -- procedure: posv object list returns integer or #f
 -- procedure: position object list returns integer or #f
     These find the position of the first element equal to OBJECT in LIST.  Posq compares elements by eq?; posv compares by eqv?; position compares by equal?.

          (posq 'c '(a b c d e f))
              => 2
          (posv 1/2 '(1 1/2 2 3/2))
              => 1
          (position '(d . e) '((a . b) (b . c) (c . d) (d . e) (e . f)))
              => 3 lisp

 -- procedure: any predicate list returns value or #f
 -- procedure: every predicate list returns boolean
     Any returns the value that PREDICATE returns for the first element in LIST for which PREDICATE returns a true value; if no element of LIST satisfied PREDICATE, any returns #f.  Every returns #t if every element of LIST satisfies PREDICATE, or #f if there exist any that do not.

          (any (lambda (x) (and (even? x) (sqrt x)))
               '(0 1 4 9 16))
              => 2
          (every odd? '(1 3 5 7 9))
              => #t lisp

 -- procedure: sublist list start end returns list
     Returns a list of the elements in LIST including & after that at the index START and before the index END.

          (sublist '(a b c d e f g h i) 3 6)      => (d e f) lisp

 -- procedure: last list returns value
     Returns the last element in LIST.  Last's effect is undefined if LIST is empty.

          (last '(a b c))                         => c lisp

 -- procedure: insert object list elt< returns list
     Inserts OBJECT into the sorted list LIST, comparing the order of OBJECT and each element by ELT<.

          (insert 3 '(0 1 2 4 5) <)               => (0 1 2 3 4 5) lisp

Filenames
---------

There are some basic filename manipulation facilities exported by the filenames structure. (The facilities Scheme48 provides are very rudimentary, and they are not intended to act as a coherent and comprehensive pathname or logical name facility such as that of Common Lisp.  However, they served the basic needs of Scheme48's build process when they were originally created.)

 -- constant: *scheme-file-type* returns symbol
 -- constant: *load-file-type* returns symbol
     *Scheme-file-type* is a symbol denoting the file extension that Scheme48 assumes for Scheme source files; any other extension, for instance in the filename list of a structure definition, must be written explicitly.  *Load-file-type* is a symbol denoting the preferable file extension to load files from.  (*Load-file-type* was used mostly in bootstrapping Scheme48 from Pseudoscheme or T long ago and is no longer very useful.)

 -- procedure: file-name-directory filename returns string
 -- procedure: file-name-nondirectory filename returns string
     File-name-directory returns the directory component of the filename denoted by the string FILENAME, including a trailing separator (on Unix, /).  File-name-nondirectory returns everything but the directory component of the filename denoted by the string FILENAME, including the extension.

          (file-name-directory "/usr/local/lib/scheme48/scheme48.image")
              => "/usr/local/lib/scheme48/"
          (file-name-nondirectory "/usr/local/lib/scheme48/scheme48.image")
              => "scheme48.image"
          (file-name-directory "scheme48.image")
              => ""
          (file-name-nondirectory "scheme48.image")
              => "scheme48.image" lisp

Namelists are platform-independent means by which to name files. They are represented as readable S-expressions of any of the following forms:

BASENAME
     represents a filename with only a basename and no directory or file type/extension;

(DIRECTORY BASENAME [TYPE])
     represents a filename with a single preceding directory component and an optional file type/extension; and

((DIRECTORY ...) BASENAME [TYPE])
     represents a filename with a sequence of directory components, a basename, and an optional file type/extension.

Each atomic component --- that is, the basename, the type/extension, and each individual directory component --- may be either a string or a symbol.  Symbols are converted to the canonical case of the host operating system by namestring (on Unix, lowercase); the case of string components is not touched.

 -- procedure: namestring namelist directory default-type returns string
     Converts NAMELIST to a string in the format required by the host operating system. (However, the current standard distribution of Scheme48 is specific to Unix: the current code implements only Unix filename facilities.)  If NAMELIST did not have a directory component, DIRECTORY, a string in the underlying operating system's format for directory prefixes, is added to the resulting namestring; and, if NAMELIST did not have a type/extension, DEFAULT-TYPE, which may be a string or a symbol and which should not already contain the host operating system's delimiter (usually a dot), is appended to the resulting namestring.

     DIRECTORY or DEFAULT-TYPE may be #f, in which case they are not prefixed or appended to the resulting filename.

          (namestring 'foo #f #f)                 => "foo"
          (namestring 'foo "bar" 'baz)            => "bar/foo.baz"
          (namestring '(rts defenum) "scheme" 'scm)
              => "scheme/rts/defenum.scm"
          (namestring '((foo bar) baz quux) "zot" #f)
              => "zot/foo/bar/baz.quux"
          (namestring "zot/foo/bar/baz.quux" #f "mumble")
              => "zot/foo/bar/baz.quux.mumble" lisp

Filename translations
.....................

Scheme48 keeps a registry of filename translations, translations from filename prefixes to the real prefixes.  This allows abstraction of actual directory prefixes without necessitating running Scheme code to construct directory pathnames (for example, in configuration files). Interactively, in the usual command processor, users can set filename translations with the ,translate; see Basic commands.

 -- procedure: translations returns string/string-alist
     Returns the alist of filename translations.

 -- procedure: set-translation! from to returns unspecified
     Adds a filename prefix translation, overwriting an existing one if one already existed.

 -- procedure: translate filename returns translated-filename
     Translates the first prefix of FILENAME found in the registry of translations and returns the translated filename.

     (set-translation! "s48" "/home/me/scheme/scheme48/scheme")
     (translate (namestring '(bcomp frame) "s48" 'scm))
         => "/home/me/scheme/scheme48/scheme/bcomp/frame.scm"
     (translate (namestring "comp-packages" "s48" 'scm))
         => "/home/me/scheme/scheme48/scheme/comp-packages.scm"
     (translate "s48/frobozz")
         => "/home/me/scheme/scheme48/scheme/frobozz"
     (set-translation! "scheme48" "s48")
     (translate (namestring '((scheme48 big) filename) #f 'scm))
         => scheme48/big/filename.scm
     (translate (translate (namestring '((scheme48 big) filename) #f 'scm)))
         => "/home/me/scheme/scheme48/scheme/big/filename.scm" lisp

     One filename translation is built-in, mapping =scheme48/ to the
     directory of system files in a Scheme48 installation, which on Unix is
     typically a directory in /usr/local/lib.

          (translate "=scheme48/scheme48.image")
              => /usr/local/scheme48/scheme48.image lisp

Fluid/dynamic bindings
----------------------

The fluids structure provides a facility for dynamically bound resources, like special variables in Common Lisp, but with first-class, unforgeable objects.

Every Multithreading,thread in Scheme48 maintains a fluid or dynamic environment.  It maps fluid descriptors to their values, much like a lexical environment maps names to their values.  The dynamic environment is implemented by deep binding and dynamically scoped.  Fluid variables are represented as first-class objects for which there is a top-level value and possibly a binding in the current dynamic environment.  Escape procedures, as created with Scheme's call-with-current-continuation, also store & preserve the dynamic environment at the time of their continuation's capture and restore it when invoked.

The convention for naming variables that are bound to fluid objects is to add a prefix of $ (dollar sign); eg, $foo.

 -- procedure: make-fluid top-level-value returns fluid
     Fluid constructor.

 -- procedure: fluid fl returns value
 -- procedure: set-fluid! fl value returns unspecified
 -- procedure: fluid-cell-ref fluid-cell returns value
 -- procedure: fluid-cell-set! fluid-cell value returns unspecified
     Fluid returns the value that the current dynamic environment associates with FL, if it has an association; if not, it returns FL's top-level value, as passed to make-fluid to create FL.  Set-fluid! assigns the value of the association in the current dynamic environment for FL to VALUE, or, if there is no such association, it assigns the top-level value of FL to VALUE.  Direct assignment of fluids is deprecated, however, and may be removed in a later release; instead, programmers should use fluids that are bound to Cells, mutable cells. Fluid-cell-ref and fluid-cell-set! are conveniences for this; they simply call the corresponding cell operations after fetching the cell that the fluid refers to by using fluid.

 -- procedure: let-fluid fluid value thunk returns values
 -- procedure: let-fluids fluid0 value0 fluid1 value1 ... thunk returns values
     These dynamically bind their fluid arguments to the corresponding value arguments and apply THUNK with the new dynamic environment, restoring the old one after THUNK returns and returning the value it returns.

     (define $mumble (make-fluid 0))

     (let ((a (fluid $mumble))
           (b (let-fluid $mumble 1
                (lambda () (fluid $mumble))))
           (c (fluid $mumble))
           (d (let-fluid $mumble 2
                (lambda ()
                  (let-fluid $mumble 3
                    (lambda () (fluid $mumble)))))))
       (list a b c d))
         => (0 1 0 3)

     (let ((note (lambda (when)
                   (display when)
                   (display ": ")
                   (write (fluid $mumble))
                   (newline))))
       (note 'initial)
       (let-fluid $mumble 1 (lambda () (note 'let-fluid)))
       (note 'after-let-fluid)
       (let-fluid $mumble 1
         (lambda ()
           (note 'outer-let-fluid)
           (let-fluid $mumble 2 (lambda () (note 'inner-let-fluid)))))
       (note 'after-inner-let-fluid)
       ((call-with-current-continuation
          (lambda (k)
            (lambda ()
              (let-fluid $mumble 1
                (lambda ()
                  (note 'let-fluid-within-cont)
                  (let-fluid $mumble 2
                    (lambda () (note 'inner-let-fluid-within-cont)))
                  (k (lambda () (note 'let-fluid-thrown)))))))))
       (note 'after-throw))
         -| initial: 0
         -| let-fluid: 1
         -| after-let-fluid: 0
         -| outer-let-fluid: 1
         -| inner-let-fluid: 2
         -| let-fluid-within-cont: 1
         -| inner-let-fluid-within-cont: 2
         -| let-fluid-thrown: 0
         -| after-throw: 0 lisp

ASCII character encoding
------------------------

These names are exported by the ascii structure.

 -- procedure: char->ascii char returns ascii-integer
 -- procedure: ascii->char ascii-integer returns character
     These convert characters to and from their integer ASCII encodings. Char->ascii and ascii->char are similar to R5RS's char->integer and integer->char, but they are guaranteed to use the ASCII encoding.  Scheme48's integer->char and char->integer deliberately do not use the ASCII encoding to encourage programmers to make use of only what R5RS guarantees.

          (char->ascii #\a)                       => 97
          (ascii->char 97)                        => #\a lisp

 -- constant: ascii-limit returns integer
 -- constant: ascii-whitespaces returns ascii-integer-list
     Ascii-limit is an integer that is one greater than the highest number that char->ascii may return or ascii->char will accept.  Ascii-whitespaces is a list of the integer encodings of all characters that are considered whitespace: space (32), horizontal tab (9), line-feed/newline (10), vertical tab (11), form-feed/page (12), and carriage return (13).

Integer enumerations
--------------------

Scheme48 provides a facility for integer enumerations, somewhat akin to C enums.  The names described in this section are exported by the enumerated structure.

Note: These enumerations are not compatible with the Enumerated/finite types and sets, enumerated/finite type facility.

 -- syntax: define-enumeration enumeration-name (enumerand-name ...)
     Defines ENUMERATION-NAME to be a static enumeration.  (Note that it is not a regular variable.  It is actually a macro, though its exact syntax is not exposed; it must be exported with the Static type system, :syntax type.) ENUMERATION-NAME thereafter may be used with the enumeration operators described below.

 -- syntax: enum enumeration-name enumerand-name returns enumerand-integer
 -- syntax: components enumeration-name returns component-vector
     Enum expands to the integer value represented symbolically by ENUMERAND-NAME in the enumeration ENUMERATION-NAME as defined by define-enumeration.  Components expands to a literal vector of the components in ENUMERATION-NAME as defined by define-enumeration.  In both cases, ENUMERAND-NAME must be written literally as the name of the enumerand; see name->enumerand for extracting an enumerand's integer given a run-time symbol naming an enumerand.

 -- syntax: enumerand->name enumerand-integer enumeration-name returns symbol
 -- syntax: name->enumerand enumerand-name enumeration-name returns integer-enumerand
     Enumerand->name expands to a form that evaluates to the symbolic name that the integer value of the expression ENUMERAND-INTEGER is mapped to by ENUMERATION-NAME as defined by define-enumeration.  Name->enumerand expands to a form that evaluates to the integer value of the enumerand in ENUMERATION-NAME that is represented symbolically by the value of the expression ENUMERAND-NAME.

The enum-case structure provides a handy utility of the same name for dispatching on enumerands.

 -- syntax: enum-case

          (enum-case ENUMERATION-NAME KEY
            ((ENUMERAND-NAME ...) BODY)
            ...
            [(else ELSE-BODY)]) lisp
          Matches KEY with the clause one of whose names maps in
          ENUMERATION-NAME to the integer value of KEY.  KEY
          must be an exact, non-negative integer.  If no matching clause is
          found, and ELSE-BODY is present, enum-case will evaluate
          ELSE-BODY; if ELSE-BODY is not present, enum-case
          will return an unspecific value.

Examples:

     (define-enumeration foo
       (bar
        baz))

     (enum foo bar)                          => 0
     (enum foo baz)                          => 1

     (enum-case foo (enum foo bar)
       ((baz) 'x)
       (else  'y))
         => y

     (enum-case foo (enum foo baz)
       ((bar) 'a)
       ((baz) 'b))
         => b

     (enumerand->name 1 foo)                 => baz
     (name->enumerand 'bar foo)              => 0
     (components foo)                        => #(bar baz) lisp

          [file data.texi not included]

Type annotations
----------------

Scheme48 allows optional type annotations with the loophole special form from the loopholes structure.

 -- syntax: loophole type expression returns values
     This is exactly equivalent in semantics to EXPRESSION, except the static type analyzer is informed that the whole expression has the type TYPE.  For details on the form of TYPE, see Static type system.

Type annotations can be used for several different purposes:

  * simply to give more information to the static type analyzer;

  * to work as a simple abstract data type facility: passing a type name that does not already exist creates a new disjoint value type; and

  * to prevent the type system from generating warnings in the rare cases where it would do so incorrectly, such as in the primitive-cwcc, primitive-catch, and with-continuation devices (to be documented in a later edition of this manual).

To see an example of the second use, see rts/jar-defrecord.scm in Scheme48's source tree.

Note: Type annotations do not damage the safety of Scheme's type system.  They affect only the static type analyzer, which does not change run-time object representations; it only checks type soundness of code and generates warnings for programs that would cause run-time type errors.

Explicit renaming macros
------------------------

Scheme48 supports a simple low-level macro system based on explicitly renaming identifiers to preserve hygiene.  The macro system is well-integrated with the module system; see Macros in concert with modules.

Explicit renaming macro transformers operate on simple S-expressions extended with identifiers, which are like symbols but contain more information about lexical context.  In order to preserve that lexical context, transformers must explicitly call a renamer procedure to produce an identifier with the proper scope. To test whether identifiers have the same denotation, transformers are also given an identifier comparator.

The facility provided by Scheme48 is almost identical to the explicit renaming macro facility described in [Clinger 91]. (For the sake of avoiding any potential copyright issues, the paper is not duplicated here, and instead the author of this manual has written the entirety of this section.)  It differs only by the transformer keyword, which is described in the paper but not used by Scheme48, and in the annotation of auxiliary names.

 -- syntax: define-syntax name transformer [aux-names]
     Introduces a derived syntax NAME with the given transformer, which may be an explicit renaming transformer procedure, a pair whose car is such a procedure and whose cdr is a list of auxiliary identifiers, or the value of a syntax-rules expression.  In the first case, the added operand AUX-NAMES may, and usually should except in the case of local (non-exported) syntactic bindings, be a list of all of the auxiliary top-level identifiers used by the macro.

Explicit renaming transformer procedures are procedures of three arguments: an input form, an identifier renamer procedure, and an identifier comparator procedure.  The input form is the whole form of the macro's invocation (including, at the car, the identifier whose denotation was the syntactic binding).  The identifier renamer accepts an identifier as an argument and returns an identifier that is hygienically renamed to refer absolutely to the identifier's denotation in the environment of the macro's definition, not in the environment of the macro's usage.  In order to preserve hygiene of syntactic transformations, macro transformers must call this renamer procedure for any literal identifiers in the output.  The renamer procedure is referentially transparent; that is, two invocations of it with the same arguments in terms of eq? will produce the same results in the sense of eq?.

For example, this simple transformer for a swap! macro is incorrect:

     (define-syntax swap!
       (lambda (form rename compare)
         (let ((a (cadr  form))
               (b (caddr form)))
           `(LET ((TEMP ,a))
              (SET! ,a ,b)
              (SET! ,b TEMP))))) lisp

     The introduction of the literal identifier temp into the output
     may conflict with one of the input variables if it were to also be
     named temp: (swap! temp foo) or (swap! bar temp)
     would produce the wrong result.  Also, the macro would fail in another
     very strange way if the user were to have a local variable named
     let or set!, or it would simply produce invalid output if
     there were no binding of let or set! in the environment
     in which the macro was used.  These are basic problems of abstraction:
     the user of the macro should not need to know how the macro is
     internally implemented, notably with a temp variable and using
     the let and set! special forms.

     Instead, the macro must hygienically rename these identifiers using
     the renamer procedure it is given, and it should list the top-level
     identifiers it renames (which cannot otherwise be extracted
     automatically from the macro's definition):

          (define-syntax swap!
            (lambda (form rename compare)
              (let ((a (cadr  form))
                    (b (caddr form)))
                `(,(rename 'LET) ((,(rename 'TEMP) ,a))
                   (,(rename 'SET!) ,a ,b)
                   (,(rename 'SET!) ,b ,(rename 'TEMP)))))
            (LET SET!)) lisp

          However, some macros are unhygienic by design, ie they insert
          identifiers into the output intended to be used in the environment of
          the macro's usage.  For example, consider a loop macro that
          loops endlessly, but binds a variable named exit to an escape
          procedure to the continuation of the loop expression, with
          which the user of the macro can escape the loop:

               (define-syntax loop
                 (lambda (form rename compare)
                   (let ((body (cdr form)))
                     `(,(rename 'CALL-WITH-CURRENT-CONTINUATION)
                        (,(rename 'LAMBDA) (EXIT)      ; Literal, unrenamed EXIT.
                          (,(rename 'LET) ,(rename 'LOOP) ()
                            ,@body
                            (,(rename 'LOOP)))))))
                 (CALL-WITH-CURRENT-CONTINUATION LAMBDA LET)) lisp

               Note that macros that expand to loop must also be unhygienic;
               for instance, this naïve definition of a
               loop-while macro is incorrect, because it hygienically renames
               exit automatically by of the definition of syntax-rules,
               so the identifier it refers to is not the one introduced
               unhygienically by loop:

                    (define-syntax loop-while
                      (syntax-rules ()
                        ((LOOP-WHILE test body ...)
                         (LOOP (IF (NOT test)
                                   (EXIT))                  ; Hygienically renamed.
                               body ...)))) lisp

                    Instead, a transformer must be written to not hygienically rename
                    exit in the output:

                         (define-syntax loop-while
                           (lambda (form rename compare)
                             (let ((test (cadr form))
                                   (body (cddr form)))
                               `(,(rename 'LOOP)
                                  (,(rename 'IF) (,(rename 'NOT) ,test)
                                    (EXIT))                      ; Not hygienically renamed.
                                  ,@body)))
                           (LOOP IF NOT)) lisp

                         To understand the necessity of annotating macros with the list of
                         auxiliary names they use, consider the following definition of the
                         delay form, which transforms (delay EXP) into
                         (make-promise (lambda () EXP)), where make-promise
                         is some non-exported procedure defined in the same module as the
                         delay macro:

                              (define-syntax delay
                                (lambda (form rename compare)
                                  (let ((exp (cadr form)))
                                    `(,(rename 'MAKE-PROMISE) (,(rename 'LAMBDA) () ,exp))))) lisp

                              This preserves hygiene as necessary, but, while the compiler can know
                              whether make-promise is exported or not, it cannot in
                              general determine whether make-promise is local, ie
                              not accessible in any way whatsoever, even in macro output, from any
                              other modules.  In this case, make-promise is not local,
                              but the compiler cannot in general know this, and it would be an
                              unnecessarily heavy burden on the compiler, the linker, and related
                              code-processing systems to assume that all bindings are not local.  It
                              is therefore better (However, the current compiler in Scheme48
                              does not require this, though the static linker does.) to annotate such
                              definitions with the list of auxiliary names used by the transformer:

                                   (define-syntax delay
                                     (lambda (form rename compare)
                                       (let ((exp (cadr form)))
                                         `(,(rename 'MAKE-PROMISE) (,(rename 'LAMBDA) () ,exp))))
                                     (MAKE-PROMISE LAMBDA)) lisp