porky11
porky11

Reputation: 962

Handle lambda-lists in method defining macros

for example I want to get all variables from a lambda-list to define a macro:

(defmacro my-defun (name lambda-list &body body &aux (fname (gensym)))
  `(progn
     (defun ,fname ,(all-vars lambda-list)
       ,@body)
     (defun ,name ,lambda-list
       (,fname ,@(all-vars lambda-list)))))

are there some functions to do this or or other things to handle easier with lambda-lists, or is there no easier way than writing functions like this:

(defun all-vars (lambda-list)
  (mapcar (lambda (cons) (if (consp cons) (car cons) cons))
          (remove-if (lambda (symbol) (and (symbolp symbol)
                                           (char= (char (symbol-name symbol) 0) #\&)))
                     lambda-list)))

Upvotes: 2

Views: 169

Answers (2)

Baggers
Baggers

Reputation: 3231

So many times I write a function like this only to find out it is already in the alexandria library.

(ql:quickload :alexandria)

And then

CL-USER> (alexandria:parse-ordinary-lambda-list '(a b c &optional x &key (ham 1 used-ham)))

returns

(A B C)
((X NIL NIL))
NIL
(((:HAM HAM) 1 USED-HAM))
NIL
NIL 
T

Upvotes: 1

Joshua Taylor
Joshua Taylor

Reputation: 85883

This approach is sort of what you have to do, or need to find a library that already implements it. There are libraries out there that handle lambda-list parsing (and every implementation has to do it, at least internally). It's not usually too hard, just tedious.

That said, your implementation of all-vars isn't quite correct. The full specification for keyword arguments in ordinary lambda lists is:

[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]] 

That means you can, e.g.,

(defun (&key ((:long-keyword-name x) "EX" xp))
  ...)

When you do (if (consp cons) (car cons) cons), you'd get (:long-keyword-name x) back, not x.

Here's a version that I think works, but haven't tested extensively:

(defun lambda-list-variables (lambda-list)
  "Returns, in order, the variables declared in an ordinary lambda list,
but doesn't check that the lambda-list is actually legal."
  (labels ((ll-keyword-p (x)
             (member x lambda-list-keywords))
           (to-var (x)
             (if (symbolp x)
                 x             ; required, optional, rest, key, and aux 
                 (destructuring-bind (var &rest init-and-supplied) x
                   (declare (ignore init-and-supplied))
                   (if (listp var)
                       (second var)     ; key vars
                       var)))))         ; optional, key, and aux vars
    (remove-if #'ll-keyword-p
               (mapcar #'to-var lambda-list))))

CL-USER> (lambda-list-variables
          '(a b 
            &optional c (d 'dee) (e 'ee e-p)
            &rest f
            &key
            g
            (h 'aitch)
            (i 'eye i-p)
            ((:jay j))
            ((:kay k) 'kay)            
            ((:ell l) 'ell l-p)
            &aux 
            m
            (n 'en)))
;=> (A B C D E F G H I J K L M N)

Upvotes: 2

Related Questions