At 11:08 AM 8/18/98 +0200, Michael Sperber [Mr. Preprocessor] wrote:
>>>>> "Craig" == Craig Lanning
<CraigL(a)internetx.net> writes:
Craig> At 10:01 AM 8/17/98 +0200, Michael Sperber [Mr. Preprocessor] wrote:
>> This indeed shows the CONS example to be poor. The point remains the
>> same, however: What if we bind a variable bound in a macro. Say,
>> *features*? What if a temporary identifier in a macro definition
>> shadows a binding in a use?
Craig> I think I see what you're driving at, but I'm not sure that it's as
Craig> important as it's being made out to be. It's easy enough to use
Craig> MAKE-SYMBOL to create safe variable names for the macro and still
Craig> maintain readability.
Sure, but there's no guarantee that a given macro has hygienic
behavior. Generally, maintaining hygiene this way is pretty tedious,
which is why programmers usually don't go to the necessary lengths.
Maintaining hygiene this way is not really much worse than writing the
macro in the first place.
Also, a macro might refer to bindings whose names are free in the
macro definition. There's no guarantee that you'll get the same
bindings for a use that you referred to at the point of definition.
Most of the macros that I write are used at top level so lexical bindings
aren't a big problem. On the few occasions that I write a macro to use
inside other code, it is more like with-open-file so necessary variable
names are given to the macro.
Craig> It's also easy enough to expand (via MACROEXPAND)
Craig> the macro to see what it will look like (Zmacs will let me do this with
Craig> control-shift-M and meta-shift-M).
This severely restricts the complexity of macros I can use safely.
Also, the whole point of macros is syntactic abstraction. I don't
want to have to expand it to see if it does something evil.
I don't see how using macroexpand limits the complexity of macros that one can
write. (c-sh-M expands one level, m-sh-M expands all levels) I use macroexpand
to make sure that the macro expands into the code that I intended. Getting
the right things quoted can be very tricky.
>> In the examples, I don't *need* to use CONS, it just
might happen to
>> be a convenient name.
Craig> Expanding the macro should tell you everything that you need to know.
As I said, having to expand the macro is totally counterproductive.
The great thing about Scheme macros is that I basically never have to
do this. I certainly never have to do it in order to check hygiene.
(Moreover, expanding the macros cannot tell two uninterned symbols
with the same name apart, can it?)
We've been using trivial examples so far. I think that it's time to insert
a more realistic and practical example.
This is a macro that I use to expand an abstract object definition
into all the necessary class and method definitions for the application:
(defmacro DEFINE-ENTITY (name supertypes attributes &body options)
(declare #+Genera (zwei:indentation 2 7 3 1)
(ignore options))
`(progn
(defclass ,name ,(or supertypes '(entity))
,(loop for (name nil opt?) in attributes
collect `(,name
:accessor ,name
,@(unless opt? '(:initform nil))
:initarg ,(intern (cl:string name) :keyword)))
)
,@(loop for (aname type opt?) in attributes
as bname = (intern (format nil "~A-BOUNDP" aname))
as muname = (intern (format nil "~A-MAKUNBOUND" aname))
as ptype = (if (listp type)
`(clim:sequence ,(cl:second type))
type)
append `((defmethod ,bname ((obj ,name)) (slot-boundp obj ',aname))
(defmethod ,muname ((obj ,name)) (slot-makunbound obj ',muname))
(defmethod EDIT-ATTRIBUTE ((obj ,name) (attrib (eql ',aname)) stream
&rest options)
(values
(apply #'clim:accept ',(if opt? `(clim:null-or-type ,ptype) ptype)
:stream stream options)
',opt?))))
(defmethod %%ATTRIBUTES append ((obj ,name))
'(,@(loop for (aname) in attributes collect aname)))
)
)
The symbols STRING and SECOND are qualified because they have been shadowed
in this package. You will note that there are no uses of MAKE-SYMBOL or
GENSYM in this macro. Would hygienic macros have made writing this any
easier?
In case you want to expand this macro, here are some sample uses:
(define-entity managed-design-object ()
()
)
(define-entity adjacent-stratum-surface-definition (managed-design-object)
((name string)
(precedent-surface stratum-surface)
(subsequent-surface stratum-surface))
)
>> What I would also want is, that, if a macro is defined within
a
>> certain package, and I refer to bindings from the macro, that these
>> bindings are relative to the package of definition rather than the
>> package of use, obeying the standard rules of lexical scoping. Not
>> having this (as in CL) introduces other dangers.
Craig> I'm not sure what "bindings from the macro" you are referring to.
Craig> All symbols in the macro definition are relative to the package of
Craig> definition and not the package of use. I depend on this for my most
Craig> common use of macros.
Sorry, I should have been more specific. Here's an example not even
involving explicit packages:
Let's say I have this:
(defvar bar 23)
and I want to refer to bar (that is, the above binding) in a macro
body:
(defmacro foo () 'bar)
... but I do:
(let ((bar 42)) (foo))
=> 42
This violates lexical scoping.
I can't think of any practical use for this construct. When I
reference a global binding this way in a macro, it is because I
wanted to rebind it for other code within the body of the macro.
--
Cheers =8-} Chipsy
Friede, Völkerverständigung und überhaupt blabla
Craig Lanning
E-Mail: CraigL(a)InternetX.net