Path: utzoo!news-server.csri.toronto.edu!cs.utexas.edu!usc!snorkelwacker.mit.edu!bloom-beacon!dont-send-mail-to-path-lines From: plogan@dad.mentor.COM (Patrick Logan) Newsgroups: comp.lang.scheme Subject: Macros Message-ID: <9103081720.AA01902@dad.MENTOR.COM> Date: 8 Mar 91 17:20:59 GMT Sender: daemon@athena.mit.edu (Mr Background) Organization: The Internet Lines: 131 I thought this was a good, concise philosophy for using macros in LISP (and since Scheme *is* a LISP, right? :)... Please be aware that the following is not my writing, but is forwarded from Kevin Gallagher's mail on the info-macl mail list. In the past on other lists, I've forwarded things with credits well placed at the beginning and still received responses written as if I was the original author. Kevin Gallagher's mail is written with CommonLISP in mind, but the basic principles apply to Scheme. I tend to subscribe to the basic principles. I don't like some of the particular CommonLISP forms like push, pop, incf, shiftf, etc. I prefer interfaces to abstract data objects using functions rather than macros even when there are side effects to those objects. **** FORWARDED MAIL **** FORWARDED MAIL **** FORWARDED MAIL **** Date: Wed, 6 Mar 91 13:54:09 EST Reply-To: uunet!cs.umass.edu!Gallagher From: Kevin Gallagher To: info-macl@cambridge.apple.COM Subject: Re: Using Macros in Lisp More general comments on macros. Careful use of macros makes source code much easier to read and because of that your programs will be easier to debug. For example, which of the following is easier to read? (with-open-file (stream file :direction :output) (write-stuff-to-stream stream)) or (let ((stream (open file :direction :output)) (status :abort)) (unwind-protect (progn (process-file stream) (setf status :ok)) (close stream :abort (not (eq status :ok))))) This example also illustrates the primary correct use of macros, namely, to define new syntax which captures a common control abstraction. Other examples of this are dotimes, dolist, cond, etc. Similarly, macros are used to capture common programming idioms, like push, pop, incf, shiftf, etc. These could not be written without macros, because you need to get the argument place forms, not just the values. The other major use of macros is creating top-level definition forms like defun, defstruct, defclass, etc. Again, which would you rather read: (defclass a () ((slot1 :initform nil)) (:documentation "A class")) or (define-class 'a 'standard-class nil '((:name s1 :initfuncion #'(lambda () nil))) '((:documentation "A class"))) A popular misconception is that macros are used `for efficiency.' As has been pointed out both here and in comp.lang.lisp, macros are not functions and can not be used as functional arguments. (E.g., you can't apply a macro or use it as a :test argument to a sequence function.) So, if you are worried about efficiency, don't write: (defmacro frame-name (x) `(car ,x)) instead write this: (proclaim (inline frame-name)) (defun frame-name (x) (car x)) By writing a function you can then do things like: (find "Dog" list-of-frames :key #'frame-name :test #'string=) (mapcar #'frame-name list-of-frames) Unfortunately, I don't know of any text that describes writing anything other than trivial macros. Probably, the best thing to do is to look at code written by good lisp programmers. Important things to consider when writing macros are: -- Take care with local variables introduced by the macro. (i.e., use gensyms for any new locals) -- Take care to preserve the left to right order of argument evaluation. -- Don't evaluate argument forms more than once. -- Know the difference between read time, macro expansion time, compile time, and load time. -- Don't do any side effects in your macro. A macro is just a source to source transformation. -- Make sure the macroexpansion doesn't cause spurious compiler warnings. In addition your macro should fit in with the language. For example, if you write a macro (WITH-FROB ) it should return the value of the last form in body -- not the value of whatever cleanup forms you need to do. Macros are a powerful tool for writing clear, abstract, concise code -- but they are certainly not the only one. When used correctly they help you to concentrate on the problem rather than worrying about the implementation details. Knowing when macros are appropriate only comes with experience. (I hope this doesn't come off as preachy.) Kevin Gallagher Gallagher@cs.umass.edu