home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!elroy.jpl.nasa.gov!swrinde!cs.utexas.edu!sun-barr!olivea!mintaka.lcs.mit.edu!gateway
- From: KMP@stony-brook.scrc.symbolics.com (Kent M Pitman)
- Newsgroups: comp.lang.lisp
- Subject: Re: Macro lambda lists: &key and &body
- Message-ID: <19920902214912.7.KMP@PLANTAIN.SCRC.Symbolics.COM>
- Date: 2 Sep 92 21:49:21 GMT
- References: <MOORE.92Aug27135541@defmacro.cs.utah.edu>
- Sender: news@mintaka.lcs.mit.edu
- Organization: LCS news/mail gateway
- Lines: 97
- X-Unparseable-Date: Wed
-
-
- Date: Thu, 27 Aug 1992 13:55 EDT
- From: Tim Moore <moore@cs.utah.edu>
-
- In article <1992Aug27.192017.18279@cs.cornell.edu> raman@cs.cornell.edu (T. V. Raman) writes:
- ...
- Lisp does not permit
- (defmacro (&key (..) &body body)
- ...)
-
- What is the correct way of writing this?
-
- (defmacro foo ((&key ...) &body body) ...)
-
- Tim's suggestion involves changing the problem specification, since you need
- to write (FOO (:key1 val1 :key2 val2) ..body..) instead of
- (FOO :key1 val1 :key2 val2 ..body..).
- If you can get away with doing that, I recommend his approach as cleanest and
- simplest. However, there are a few cases where you perhaps won't want to do
- this change, and a few others where perhaps you can't (due to externally imposed
- constraints.) If you can't change the problem description, what you have to do
- is something like the following:
-
- (defmacro foo (&rest keys-and-body)
- (let ((body keys-and-body)
- (my-key-1)
- (my-key-1-p nil)
- (my-key-2)
- (my-key-2-p nil))
- (loop
- (unless (and body (cdr body)) (return))
- (case (car body)
- ((:my-key-1)
- (unless my-key-1-p
- (setq my-key-1 (cadr body) my-key-1-p t)))
- ((:my-key-2)
- (unless my-key-2-p
- (setq my-key-2 (cadr body) my-key-2-p t)))
- (otherwise (return)))
- (setq body (cddr body)))
- (unless my-key-1-p (setq my-key-1 <my-key-1-default>))
- (unless my-key-2-p (setq my-key-2 <my-key-2-default>))
- ; Beyond here, pretend arglist was effectively
- ; (&key (my-key-1 <my-key-1-default> my-key-1-p)
- ; (my-key-2 <my-key-2-default> my-key-2-p)
- ; &body body)
- ...))
-
- My point here is that Lisp doesn't forbid you from having things with
- this calling convention. It just happens not to provide you with
- built-in support for parsing those things. But that doesn't mean you
- can't write the parsing support yourself. This technology is effectively
- what is needed in the RESTART-CASE macro, which takes keywords in this way
- in its clauses.
-
- Note that if you do this a lot, you could imagine modularizing it more so
- that all you could share the work needed to do the parsing among all the
- clients. e.g.,
-
- ;;; General parsing utility
- (defun call-with-body-and-prefix-keywords (continuation body keys)
- (declare (dynamic-extent continuation))
- (let ((parsed-keys '()))
- (loop
- (unless (and body (cdr body)
- (member (car body) keys))
- (return))
- (let ((key (car body)))
- ;; Assure left-most element supersedes right in case of duplication
- (setf (getf parsed-keys key) (getf parsed-keys key (cadr body))))
- (setq body (cddr body)))
- (apply continuation body parsed-keys)))
-
- ;;; Sample client
- (defmacro foo (&rest keys-and-body)
- (call-with-body-and-prefix-keywords
- #'(lambda (body &key my-key-1 my-key-2)
- `(list 1 ,my-key-1 2 ,my-key-2 :body ',body))
- keys-and-body
- '(:my-key-1 :my-key-2)))
-
- ;;; Sample call
- (foo :my-key-2 7 :my-key-1 3 :my-key-1 4 foo bar baz)
- => (1 3 2 7 :BODY (FOO BAR BAZ))
-
- Note that this solution piggy-backs off of the supplied-p and default value
- mechanism already present in the langauge, and doesn't require explicit support
- from the CALL-WITH-BODY-AND-PREFIX-KEYWORDS routine in order to do that parsing.
-
- You could even write a definer that inferred the call to this subroutine,
- such that all you had to write was:
-
- (defmacro-with-body-and-prefix-keywords foo (body &key my-key-1 my-key-2)
- `(list 1 ,my-key-1 2 ,my-key-2 :body ',body))
-
- and it would infer the rest. I'll leave the definition of
- DEFMACRO-WITH-BODY-AND-PREFIX-KEYWORDS as an exercise to the reader.
-