[comp.lang.scheme] Macros; lexcial scope

net@TUB.BITNET (Oliver Laumann) (05/25/88)

I would like to know how you would expect macros to work in Scheme
or generally in a lexically scoped Lisp.
(I'm not talking about a specific Scheme implementation here)

Suppose Scheme supported something like ``define-macro'' with the same
syntax as ``define'', so that you could, for instance, write

   (define-macro (incr x) `(set! ,x (+ ,x 1)))

Now my question is whether macros are lexically scoped (like functions)
or whether macro expansion is performed in a purely syntactic way.

Consider the following example:

   (define x 1)
   (define-macro (m) x)

   (let ((x 2))
     (m))

Would you expect that (m) evaluates to 1 or to 2?  Yes, I know, if I had
written (define-macro (m) 'x), it would evaluate to 2.  However, in
both Common Lisp (using a different syntax, of course) and C-Scheme,
(m) returns 1 in the example above.
Thus, it looks as if the first time the macro body is evaluated, the
evaluation takes place in the lexical scope of the define-macro.

Now consider a slighly more complex example:

   (define x 1)

   (let ((x 2))
     (define-macro (m) x)
     (let ((x 3))
       (m)))

In both Common Lisp and C-Scheme, this evaluates to 1.  My mind boggles.
The macro is local to the outer let, why is the ``x'' inside the macro
body bound to the global variable?  Are macro always implemented like
this (in lexically scoped Lisps)?  If so, why?

[I'm sorry, if the answer to my question is obvious; I'm new to Scheme
and Lisp.]

--
Regards,
    Oliver Laumann, Technical University of Berlin, Germany.
    ...!pyramid!tub!net   or   net@TUB.BITNET
    ...!mcvax!unido!tub!net

krulwich-bruce@CS.YALE.EDU (Bruce Krulwich) (05/26/88)

In article <8805251659.AA00682@tub.UUCP> net@TUB.BITNET (Oliver Laumann)
writes:
>Consider the following example:
>   (define x 1)
>   (define-macro (m) x)
>   (let ((x 2))
>     (m))
>Would you expect that (m) evaluates to 1 or to 2?  In both Common Lisp
>(using a different syntax, of course) and C-Scheme, (m) returns 1 in the
>example above.  Thus, it looks as if the first time the macro body is
>evaluated, the evaluation takes place in the lexical scope of the
>define-macro.

Rather, that the macro is evaluated in the lexical scope in which it was 
defined, just like regular functions.

>Now consider a slighly more complex example:
>   (define x 1)
>   (let ((x 2))
>     (define-macro (m) x)
>     (let ((x 3))
>       (m)))
>In both Common Lisp and C-Scheme, this evaluates to 1.

T's DEFINE-SYNTAX does what you want in both of these cases, evaluating to 1
in the first case and 2 in the second case.  


Bruce Krulwich

cph@KLEPH.AI.MIT.EDU (Chris Hanson) (05/27/88)

   Date: Wed, 25 May 88 18:59:30 +0200
   From: Oliver Laumann <net%TUB.BITNET@MITVMA.MIT.EDU>

   Consider the following example:

      (define x 1)
      (define-macro (m) x)

      (let ((x 2))
	(m))

   in both Common Lisp (using a different syntax, of course) and
   C-Scheme, (m) returns 1 in the example above.

You have an old version of C-Scheme.  The current version (release 6
and later) gives an "unbound variable X" error while evaluating the
last expression.

   Now consider a slighly more complex example:

      (define x 1)

      (let ((x 2))
	(define-macro (m) x)
	(let ((x 3))
	  (m)))

   In both Common Lisp and C-Scheme, this evaluates to 1.

Again, this produces an "unbound variable X" error in the current
release.

As one of several people who has thought about the issues you raise,
I'll offer the following:

1. The semantics of special forms like `define-macro' is confusing.
The naive interpretation of the meaning requires that a macro created
this way should be closed in the environment in which it lexically
appears.  Unfortunately, that environment usually does not exist until
run time, while for obvious reasons it is desirable that macros be
closed at compile time.

2. C-Scheme has "solved" this problem by forcing all such macros to be
closed in the global environment (actually, in a distinct child of the
global environment).  This alternate environment is guaranteed to
exist at compile time, and under normal circumstances your other
definitions won't interact with it.  Thus, this explains the "unbound
variable" errors.  I say "solved" above because, rather than providing
an acceptable solution, all we've really done is try to make it
obvious when the user is confused about the closing environment.

3. My personal conclusion is that `define-macro' is a bad idea.  I
rarely, if ever, use it.  C-Scheme provides facilities for explicitly
constructing syntax tables (see the file "runtime/syntax.scm",
procedures `make-syntax-table' and `syntax-table-define', and the
special forms `macro' and `using-syntax') which do not have this
problem.  Using these facilities, the macros are closed in the
environment in which they are loaded.  They also appear in a different
file from their references, thus reducing the confusion.

4. The R*RS committee is in the process of generating a "standard"
macro facility.  Keep tuned for more news.