Subject: Re: Macro-writing in CL
From: rpw3@rigden.engr.sgi.com (Rob Warnock)
Date: 12 Jun 2001 02:52:30 GMT
Newsgroups: comp.lang.lisp
Message-ID: <9g405e$fu98h$1@fido.engr.sgi.com>
Kent M Pitman  <pitman@world.std.com> wrote:
+---------------
| You mean to say that no Lisp1-er, even in the automobile industry,
| ever does:
|    (defun foo (car) ...) / (define (foo car) ...)
| without fear that some macro will use CAR free?
+---------------

I'm afraid that sounds like a "Have you stopped beating your wife?"
question to me. No sane Schemer would ever bind "car", with or without
macros!

Using the term "macro" here [and other places in your argument] is mostly
a red herring. Binding "car" with a "let" or as a formal parameter (which
in Scheme are equivalent) is *ALREADY* a such a problem in a Lisp1 that
everyone who writes Scheme seriously *already* has to worry about it,
completely independently of macros. In Scheme, you simply *MUST* memorize
the entire set of builtin primitives and refrain from naming variables
with the same names, or your code -- even if it runs correctly when written
initially -- will be totally unmaintainable. Can we just *stipulate* that,
please?

For anyone who is practiced & fluent in writing Scheme and *doesn't*,
therefore, routinely step on their own d... uh, toes, in my experience
adding "defmacro" to the mix does *NOT* make the problem any worse.
As long as both the macro writer and the macro user (writer of a call)
obey the implied contract (mandatory for sanity in a Lisp1) to not shadow
"well-known" global names ("car", "list", etc.), no *new* risks are added
by the introduction of (well-written) macros that are not already present
with the cut'n'paste nature of code development & maintenance.

That is, one *already* has to be careful when cut'n'paste-ing S-exprs
from one place in the code to another (since Scheme doesn't offer an
"inline" declaration) that you don't shadow either globals or local
bindings that are used in ways inconsistent with the cut'n'paste'd code.
Macros, in fact, can make this somewhat *easier*, since you can (using
"gensym") automatically avoid inadvertent capture of variable names
introduced by the macro expansion. This is so beneficial that one
*should* liberally use macros for inlining, rather than cut'n'paste!

+---------------
| I don't agree.  I think people want to use CAR in both Lisp1 and Lisp2,
| and the way this very legitimate need is implemented in Lisp1 is by
| "macro hygiene"...
+---------------

RED HERRING!!!  People who want to write maintainable code in a Lisp1
must *not* use "car" for anything but the system primitive, and must
exercise extreme discipline in choosing variable names *everywhere*,
even in the complete absence of macros... because you never know who is
going to be cut'n'paste-ing your code next week/month/year. If you allow
yourself to shadow "car" with a "let" or argument binding, it *WILL* come
back to bite you eventually (even in the complete absence of macros).

Eric often suggests that people not bring their Scheme preconceptions
to programming CL. The reverse is also true. People who write Scheme
live with the limitations of a Lisp1 all the time. Yes, it looks painful
to a practicing CL programmer (and it is), but it's there. It requires
a certain discipline in naming, which can (IMHO) be *assumed* to be
well-ingrained by the time the issue of "defmacro"-style macros comes up.
Nevertheless, people come from a CL context and say, "Oh, horrors, what
if somebody has bound 'car' in a context in which you call a macro that
uses it?" Well, that's would be a disaster, wouldn't it, even *without*
a macro, so Schemers simply "don't do that". [That is, Schemers bind
variable-bind "car" and "list", etc., about as often as CL'ers FLET
them -- practically never.]

Since that is true (or so I would claim), "defmacro"-style macros really
don't make things any worse in Scheme, and *can* even make life easier
(such as the above-mentioned inlining).

+---------------
| Because Scheme cannot make this distinction, MY PERSONAL EXPERIENCE IN
| REAL PROGRAM WRITING, and NOT just theory, tells me that I have to make
| assumptions the macro writer for any macros in the definition of FOO and
| whether they have done
|   (defmacro next-car (cars) `(car ,cars))
+---------------

I currently write a lot more Scheme than CL (though the ratio is becoming
somewhat smaller), and in my personal experience the general hazards of
a Lisp1 *absent* macros far dominate any problems introduced by macros.
To stay sane, you simply *MUST NOT* bind "car" (etc.) in Scheme code, with
or without macros. And if you adhere to that discipline already, "defmacro"
is no worse in Scheme than in CL, and your example is just as safe as any
other part of your program would be.


-Rob

p.s. Disclaimer (confession?): I never really got comfortable with the
R5RS macros. "Defmacro" is the *only* macro style I ever use in Scheme.
[All of the Schemes I use either have it or have something from which it
can be trivially defined.]  I understand "defmacro" ("code that writes
code", etc.), and understand how to get the kinds of hygiene I need from it.

-----
Rob Warnock, 31-2-510		<rpw3@sgi.com>
SGI Network Engineering		<http://reality.sgi.com/rpw3/> [until 8/15]
1600 Amphitheatre Pkwy.		Phone: 650-933-1673
Mountain View, CA  94043	PP-ASEL-IA

[Note: aaanalyst@sgi.com and zedwatch@sgi.com aren't for humans ]