[gambit-list] question from gambit-list

Marc Feeley feeley at iro.umontreal.ca
Tue Mar 6 14:20:46 EST 2012


On 2012-03-06, at 10:44 AM, Álvaro Castro-Castilla wrote:

> Thank you Marc!
> Well, that certainly gives me a lot of possibilities, so I can keep
> using syntax-rules.
> 
> But I didn't state my question well. The thing that I don't understand
> is why when loading the psyntax extension, these files here work, but
> without psyntax, the push! macro is undefined. What's modifying
> psyntax that makes something that doesn't work in psyntax-less gambit
> actually work.
> I was thinking that it might do with environments, but macros are all
> in the same environment, right?
> 
> 
> alvatar at asia~ $ cat testA.scm
> (define-macro (push! list obj)
>             `(set! ,list (cons ,obj ,list)))
> 
> (load "testB")
> 
> alvatar at asia~ $ cat testB.scm
> (define l '(b c))
> (push! l 'a)
> (pp l)
> 
> alvatar at asia~ $ gsc -e '(load "testA")'
> *** ERROR IN "testB.scm"@2.2 -- Unbound variable: push!
> 
> alvatar at asia~ $ gsc -:s -e '(load "testA")'
> (a b c)
> 
> 
> Did I express the question properly?
> 
> Thanks again for your help and invaluable explanations, Marc.

As I have said previously the psyntax macro-expander creates a new dialect of Scheme on top of Gambit.  The macros that are defined when using syntax-rules and syntax-case are completely handled by psyntax, and the Gambit macro-expansion system is *not* used.  Even the define-macro is rewritten into a use of define-syntax and syntax-case.  The difference in behaviour that you are witnessing is due to the way the psyntax authors believe macros should work.  In terms of implementation there is probably one global environment where toplevel macros are registered.  So a macro defined in one file, will be visible in another file that is "load"ed.  This is fine for an interpreter or a system like ChezScheme which uses a just-in-time compiler (Scheme code is compiled in the order that it is visited, either at the REPL or in files that are "load"ed).

This model is not well adapted for Gambit which compiles files ahead of time.  So Gambit uses a model where macro definitions are local to each compilation unit (similarly to macros in the C language).  If you define macro M in a Scheme file A.scm, it is only visible in the file A.scm .  However, Gambit has an "include" special form which allows splicing Scheme code from a file into multiple other Scheme files.  If (include "A.scm") is used in file B.scm, then macro M defined in A.scm will be visible in B.scm (simply because the include essentially duplicates the code into B.scm).

Note that there is a *huge* difference between a (include "A.scm") and (load "A.scm") in B.scm .  The load *procedure* is called when the program is executed to load the code from A.scm, whereas the include *special form* is handled at macro expansion time, so the code from A.scm, including the macro definition of M, is visible to the Scheme compiler.  Imagine the following Scheme program B.scm:

;; B.scm
(load (string-append (read) ".scm"))
(print (M 1 2 3))

Let's say you compile this program with

   gsc B

and then run it with

   gsi B

The program will wait until you enter a string, say "A", and then proceed to append ".scm" and load that file.  So it will load the file A.scm .  The compiler could not know that A.scm was the file that would be loaded by (load (string-append (read) ".scm")), so even though A.scm defines the macro M, it will not affect the call (M 1 2 3).  The compiler will have assumed when compiling B.scm that M is a global variable and that (M 1 2 3) is a procedure call to the procedure contained in M.

You can't do (include (string-append (read) ".scm")), because include is a special form and its sole argument must be a literal string naming the file to include.  The closest you can get is

;; B.scm
(define-macro (foo)
  `(include ,(string-append (read) ".scm")))
(foo)
(print (M 1 2 3))

It is when you *compile* this program with

   gsc B

that the read procedure is called and you have to enter a string.  If you enter "A", then the file "A.scm" will be included and the call (M 1 2 3) will expand accordingly to the macro definition of M in A.scm .  No string will be read when you execute the program with

   gsi B

In other words, load and include have completely different binding times in Gambit.

Does this clarify things?

Marc




More information about the Gambit-list mailing list