home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #19 / NN_1992_19.iso / spool / comp / lang / lisp / 2362 < prev    next >
Encoding:
Text File  |  1992-09-02  |  4.0 KB  |  110 lines

  1. Path: sparky!uunet!elroy.jpl.nasa.gov!swrinde!cs.utexas.edu!sun-barr!olivea!mintaka.lcs.mit.edu!gateway
  2. From: KMP@stony-brook.scrc.symbolics.com (Kent M Pitman)
  3. Newsgroups: comp.lang.lisp
  4. Subject: Re: Macro lambda lists: &key and &body
  5. Message-ID: <19920902214912.7.KMP@PLANTAIN.SCRC.Symbolics.COM>
  6. Date: 2 Sep 92 21:49:21 GMT
  7. References: <MOORE.92Aug27135541@defmacro.cs.utah.edu>
  8. Sender: news@mintaka.lcs.mit.edu
  9. Organization: LCS news/mail gateway
  10. Lines: 97
  11. X-Unparseable-Date: Wed
  12.  
  13.  
  14.     Date: Thu, 27 Aug 1992 13:55 EDT
  15.     From: Tim Moore <moore@cs.utah.edu>
  16.  
  17.     In article <1992Aug27.192017.18279@cs.cornell.edu> raman@cs.cornell.edu (T. V. Raman) writes:
  18.     ...
  19.        Lisp does not permit
  20.        (defmacro (&key (..) &body body)
  21. ...)
  22.  
  23.        What is the correct way of writing this?
  24.  
  25.     (defmacro foo ((&key ...) &body body) ...)
  26.  
  27. Tim's suggestion involves changing the problem specification, since you need
  28. to write (FOO (:key1 val1 :key2 val2) ..body..) instead of 
  29.          (FOO  :key1 val1 :key2 val2  ..body..).
  30. If you can get away with doing that, I recommend his approach as cleanest and
  31. simplest.  However, there are a few cases where you perhaps won't want to do
  32. this change, and a few others where perhaps you can't (due to externally imposed
  33. constraints.)  If you can't change the problem description, what you have to do
  34. is something like the following:
  35.  
  36.  (defmacro foo (&rest keys-and-body)
  37.    (let ((body keys-and-body)
  38.      (my-key-1)
  39.      (my-key-1-p nil)
  40.      (my-key-2)
  41.      (my-key-2-p nil))
  42.      (loop
  43.        (unless (and body (cdr body)) (return))
  44.        (case (car body)
  45.          ((:my-key-1)
  46.       (unless my-key-1-p 
  47.         (setq my-key-1 (cadr body) my-key-1-p t)))
  48.      ((:my-key-2)
  49.       (unless my-key-2-p 
  50.         (setq my-key-2 (cadr body) my-key-2-p t)))
  51.      (otherwise (return)))
  52.        (setq body (cddr body)))
  53.      (unless my-key-1-p (setq my-key-1 <my-key-1-default>))
  54.      (unless my-key-2-p (setq my-key-2 <my-key-2-default>))
  55.      ; Beyond here, pretend arglist was effectively
  56.      ;   (&key (my-key-1 <my-key-1-default> my-key-1-p)
  57.      ;         (my-key-2 <my-key-2-default> my-key-2-p)
  58.      ;    &body body)
  59.      ...))
  60.  
  61. My point here is that Lisp doesn't forbid you from having things with
  62. this calling convention.  It just happens not to provide you with
  63. built-in support for parsing those things.  But that doesn't mean you
  64. can't write the parsing support yourself.  This technology is effectively
  65. what is needed in the RESTART-CASE macro, which takes keywords in this way
  66. in its clauses.
  67.  
  68. Note that if you do this a lot, you could imagine modularizing it more so
  69. that all you could share the work needed to do the parsing among all the
  70. clients. e.g.,
  71.  
  72. ;;; General parsing utility
  73. (defun call-with-body-and-prefix-keywords (continuation body keys)
  74.   (declare (dynamic-extent continuation))
  75.   (let ((parsed-keys '()))
  76.     (loop
  77.       (unless (and body (cdr body)
  78.             (member (car body) keys))
  79.         (return))
  80.       (let ((key (car body)))
  81.     ;; Assure left-most element supersedes right in case of duplication
  82.         (setf (getf parsed-keys key) (getf parsed-keys key (cadr body))))
  83.         (setq body (cddr body)))
  84.     (apply continuation body parsed-keys)))
  85.  
  86. ;;; Sample client
  87. (defmacro foo (&rest keys-and-body)
  88.   (call-with-body-and-prefix-keywords
  89.     #'(lambda (body &key my-key-1 my-key-2)
  90.     `(list 1 ,my-key-1 2 ,my-key-2 :body ',body))
  91.     keys-and-body
  92.     '(:my-key-1 :my-key-2)))
  93.  
  94. ;;; Sample call
  95. (foo :my-key-2 7 :my-key-1 3 :my-key-1 4 foo bar baz)
  96. => (1 3 2 7 :BODY (FOO BAR BAZ))
  97.  
  98. Note that this solution piggy-backs off of the supplied-p and default value
  99. mechanism already present in the langauge, and doesn't require explicit support
  100. from the CALL-WITH-BODY-AND-PREFIX-KEYWORDS routine in order to do that parsing.
  101.  
  102. You could even write a definer that inferred the call to this subroutine,
  103. such that all you had to write was:
  104.  
  105. (defmacro-with-body-and-prefix-keywords foo (body &key my-key-1 my-key-2)
  106.   `(list 1 ,my-key-1 2 ,my-key-2 :body ',body))
  107.  
  108. and it would infer the rest.  I'll leave the definition of 
  109. DEFMACRO-WITH-BODY-AND-PREFIX-KEYWORDS as an exercise to the reader.
  110.