>>>>> "Craig" == Craig Lanning <CraigL(a)internetx.net> writes:
Craig> At 11:08 AM 8/18/98 +0200, Michael Sperber [Mr. Preprocessor]
Craig> wrote: [ ... ]
Craig> Maintaining hygiene this way is not really much worse than writing the
Craig> macro in the first place.
Urmh, yes it is. So there :-)
>> 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.
Craig> Most of the macros that I write are used at top level so lexical bindings
Craig> aren't a big problem.
Yes they are, because the macro may refer to other toplevel lexical
bindings. I just posted (and corrected ...) and example about this.
It seems to me (I may be wrong) Common Lispers inherently avoid
situations where a macro lead to binding problems precisely because
they lead to these problems. In Scheme, I write macros which would
cause problems in Common Lisp (but don't in Scheme) all the time.
Craig> I don't see how using macroexpand limits the complexity of macros that one can
Craig> write. (c-sh-M expands one level, m-sh-M expands all levels) I use macroexpand
Craig> to make sure that the macro expands into the code that I intended. Getting
Craig> the right things quoted can be very tricky.
Exactly. Which is why in the Scheme macro system, the macro
metalanguage is not the same as the object language. No quoting you
may get wrong.
Craig> This is a macro that I use to expand an abstract object definition
Craig> into all the necessary class and method definitions for the application:
Craig> (defmacro DEFINE-ENTITY (name supertypes attributes &body options)
Craig> (declare #+Genera (zwei:indentation 2 7 3 1)
Craig> (ignore options))
Craig> `(progn
Craig> (defclass ,name ,(or supertypes '(entity))
Craig> ,(loop for (name nil opt?) in attributes
Craig> collect `(,name
Craig> :accessor ,name
Craig> ,@(unless opt? '(:initform nil))
Craig> :initarg ,(intern (cl:string name) :keyword)))
Craig> )
Craig> ,@(loop for (aname type opt?) in attributes
Craig> as bname = (intern (format nil "~A-BOUNDP" aname))
Craig> as muname = (intern (format nil "~A-MAKUNBOUND" aname))
Craig> as ptype = (if (listp type)
Craig> `(clim:sequence ,(cl:second type))
Craig> type)
Craig> append `((defmethod ,bname ((obj ,name)) (slot-boundp obj ',aname))
Craig> (defmethod ,muname ((obj ,name)) (slot-makunbound obj ',muname))
Craig> (defmethod EDIT-ATTRIBUTE ((obj ,name) (attrib (eql ',aname)) stream
Craig> &rest options)
Craig> (values
Craig> (apply #'clim:accept ',(if opt? `(clim:null-or-type ,ptype) ptype)
Craig> :stream stream options)
Craig> ',opt?))))
Craig> (defmethod %%ATTRIBUTES append ((obj ,name))
Craig> '(,@(loop for (aname) in attributes collect aname)))
Craig> )
Craig> )
Craig> The symbols STRING and SECOND are qualified because they have been shadowed
Craig> in this package. You will note that there are no uses of MAKE-SYMBOL or
Craig> GENSYM in this macro. Would hygienic macros have made writing this any
Craig> easier?
There happen to be no hygiene issues here. (Good for you ... :-} )
So, no, hygiene per se would not have made it easier. However,
pattern matching (instead of using s-exprs to represent expressions)
would have saved you all the quasiquoting trouble.
Here's an example in Scheme (taken from the Scheme 48 distribution)
which doesn't use hygiene, but does use pattern matching. It defines
macros for defining records in a convenient syntactic form:
; Copyright (c) 1993, 1994 by Richard Kelsey and Jonathan Rees.
; Copyright (c) 1996 by NEC Research Institute, Inc. See file COPYING.
; This is JAR's define-record-type, which doesn't resemble Richard's.
; There's no implicit name concatenation, so it can be defined
; entirely using syntax-rules. Example:
; (define-record-type foo :foo
; (make-foo x y)
; foo? - predicate name is optional
; (x foo-x)
; (y foo-y)
; (z foo-z set-foo-z!))
(define-syntax define-record-type
(syntax-rules ()
((define-record-type ?id ?type
(?constructor ?arg ...)
(?field . ?field-stuff)
...)
(begin (define ?type (make-record-type '?id '(?field ...)))
(define ?constructor (record-constructor ?type '(?arg ...)))
(define-accessors ?type (?field . ?field-stuff) ...)))
((define-record-type ?id ?type
(?constructor ?arg ...)
?pred
?more ...)
(begin (define-record-type ?id ?type
(?constructor ?arg ...)
?more ...)
(define ?pred (record-predicate ?type))))))
; Straightforward version
(define-syntax define-accessors
(syntax-rules ()
((define-accessors ?type ?field-spec ...)
(begin (define-accessor ?type . ?field-spec) ...))))
(define-syntax define-accessor
(syntax-rules ()
((define-accessor ?type ?field ?accessor)
(define ?accessor (record-accessor ?type '?field)))
((define-accessor ?type ?field ?accessor ?modifier)
(begin (define ?accessor (record-accessor ?type '?field))
(define ?modifier (record-modifier ?type '?field))))
((define-accessor ?type ?field)
(begin))))
Note the absence of quasiquote/unquote/unquote-splicing.
Here's macro which makes use of hygiene:
(define-syntax define-for-each
(syntax-rules ()
((define-for-each foo-for-each foo-length foo-ref)
(define (foo-for-each proc v)
(let ((z (foo-length v)))
(do ((i (- z 1) (- i 1)))
((< i 0) #f)
(proc (foo-ref v i))))))))
Note that
(define-for-each bar-for-each (lambda (dummy) z) (lambda (dummy) i))
(admittedly a contrived use) does the right thing.
>> Let's say I have this:
>>
>> (defvar bar 23) [ I meant (setq 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.
Craig> I can't think of any practical use for this construct. When I
Craig> reference a global binding this way in a macro, it is because I
Craig> wanted to rebind it for other code within the body of the macro.
Then you're not referencing the same global binding. When I reference
a global binding within a macro's lexical scope, I want to reference
that binding. Period.
--
Cheers =8-} Chipsy
Friede, Völkerverständigung und überhaupt blabla