There is nothing built into this language for this. Rainer Joswig's answer indicates that loop can do destructuring, but that doesn't do nearly as much. In an earlier version of this answer, I suggested going to the destructive lambda list and compiling a list of all the characters starting with _, and adding an ad to the form to ignore these variables. The safer version replaces every new fresh variable (so there are no duplicate variables) and ignores them all. So something like
(destructuring-bind (_a (_b c)) object c)
will expand into
(destructuring-bind (#:g1 (#:g2 c)) object (declare (ignore #:g1 #:g2)) c)
This approach will work fine if you use only the "data-oriented" described in 3.4.4.1.1 Restructuring according to Lambda Lists . However, if you use the lambda-list approach described in 3.4.4.1.2 Destructing with a Lambda list using a Lambda list , where you can use lambda list keywords such as & optional, & key, etc., then everything is much more complicated, because you do not have to replace the variables in some parts of these. For example, if you have
&optional (_x '_default-x)
then it would be normal to replace _x with something, but not _default-x , because the latter is not a template. But, in Lisp, code is data, so we can still write a macro that appears above the destructive lambda list and is replaced only in places that are templates. Here is some hairy code that does just that. This takes a function and a destructive lambda list and calls a function for each template variable in the lambda list, as well as the type of the argument (integer, mandatory, optional, etc.).
(defun map-dll (fn list) (let ((result '()) (orig list) (keywords '(&allow-other-keys &aux &body &key &optional &rest &whole))) (labels ((save (x) (push x result)) (handle (type parameter) (etypecase parameter (list (map-dll fn parameter)) (symbol (funcall fn type parameter))))) (macrolet ((parse-keyword ((&rest symbols) &body body) `(progn (when (and (not (atom list)) (member (first list) ',symbols)) (save (pop list)) ,@body))) (doparameters ((var) &body body) `(do () ((or (atom list) (member (first list) keywords))) (save (let ((,var (pop list))) ,@body))))) (parse-keyword (&whole) (save (handle :whole (pop list)))) (doparameters (required) (handle :required required)) (parse-keyword (&optional) (doparameters (opt) (if (symbolp opt) (handle :optional opt) (list* (handle :optional (first opt)) (rest opt))))) (when (and (atom list) (not (null list))) ; turn (... . REST) (setq list (list '&rest list))) ; into (... &rest REST) (parse-keyword (&rest &body) (save (handle :rest (pop list)))) (parse-keyword (&key) (doparameters (key) (if (symbolp key) (handle :key key) (destructuring-bind (keyspec . more) key (if (symbolp keyspec) (list* (handle :key keyspec) more) (destructuring-bind (keyword var) keyspec (list* (list keyword (handle :key var)) more))))))) (parse-keyword (&allow-other-keys)) (parse-keyword (&aux) (doparameters (aux) aux)) (unless (null list) (error "Bad destructuring lambda list: ~A." orig)) (nreverse result)))))
Using this, it is fairly easy to write destructuring-bind *, which replaces every template variable starting with _, with a fresh variable that will be ignored in the body.
(defmacro destructuring-bind* (lambda-list object &body body) (let* ((ignores '()) (lambda-list (map-dll (lambda (type var) (declare (ignore type)) (if (and (> (length (symbol-name var)) 0) (char= #\_ (char (symbol-name var) 0))) (let ((var (gensym))) (push var ignores) var) var)) lambda-list))) `(destructuring-bind ,lambda-list ,object (declare (ignore ,@(nreverse ignores))) ,@body)))
Now we need to consider the extensions he made:
(macroexpand-1 '(destructuring-bind* (&whole (a _ . b) c _ d &optional e (f '_f) &key g _h &aux (_i '_j)) object (list abcdefg))) ;=> (DESTRUCTURING-BIND (&WHOLE (A #:G1041 &REST B) C #:G1042 D &OPTIONAL E (F '_F) &KEY G
We did not replace anywhere where we should not (init forms, aux variables, etc.), but we took care of the places that we need. We also see this work in your example:
(macroexpand-1 '(destructuring-bind* ((_ (_ . title) (_ . description) _ (_ . video-id))) entry (list video-id title description))) ;=> (DESTRUCTURING-BIND ((