DESTRUCTURING-BIND

One last tool for slicing and dicing lists that I need to cover since you’ll need it in later chapters is the **DESTRUCTURING-BIND** macro. This macro provides a way to destructure arbitrary lists, similar to the way macro parameter lists can take apart their argument list. The basic skeleton of a **DESTRUCTURING-BIND** is as follows:

  1. (destructuring-bind (parameter*) list
  2. body-form*)

The parameter list can include any of the types of parameters supported in macro parameter lists such as **&optional**, **&rest**, and **&key** parameters.5 And, as in macro parameter lists, any parameter can be replaced with a nested destructuring parameter list, which takes apart the list that would otherwise have been bound to the replaced parameter. The list form is evaluated once and should return a list, which is then destructured and the appropriate values are bound to the variables in the parameter list. Then the body-forms are evaluated in order with those bindings in effect. Some simple examples follow:

  1. (destructuring-bind (x y z) (list 1 2 3)
  2. (list :x x :y y :z z)) ==> (:X 1 :Y 2 :Z 3)
  3. (destructuring-bind (x y z) (list 1 (list 2 20) 3)
  4. (list :x x :y y :z z)) ==> (:X 1 :Y (2 20) :Z 3)
  5. (destructuring-bind (x (y1 y2) z) (list 1 (list 2 20) 3)
  6. (list :x x :y1 y1 :y2 y2 :z z)) ==> (:X 1 :Y1 2 :Y2 20 :Z 3)
  7. (destructuring-bind (x (y1 &optional y2) z) (list 1 (list 2 20) 3)
  8. (list :x x :y1 y1 :y2 y2 :z z)) ==> (:X 1 :Y1 2 :Y2 20 :Z 3)
  9. (destructuring-bind (x (y1 &optional y2) z) (list 1 (list 2) 3)
  10. (list :x x :y1 y1 :y2 y2 :z z)) ==> (:X 1 :Y1 2 :Y2 NIL :Z 3)
  11. (destructuring-bind (&key x y z) (list :x 1 :y 2 :z 3)
  12. (list :x x :y y :z z)) ==> (:X 1 :Y 2 :Z 3)
  13. (destructuring-bind (&key x y z) (list :z 1 :y 2 :x 3)
  14. (list :x x :y y :z z)) ==> (:X 3 :Y 2 :Z 1)

One kind of parameter you can use with **DESTRUCTURING-BIND** and also in macro parameter lists, though I didn’t mention it in Chapter 8, is a **&whole** parameter. If specified, it must be the first parameter in a parameter list, and it’s bound to the whole list form.6 After a **&whole** parameter, other parameters can appear as usual and will extract specific parts of the list just as they would if the **&whole** parameter weren’t there. An example of using **&whole** with **DESTRUCTURING-BIND** looks like this:

  1. (destructuring-bind (&whole whole &key x y z) (list :z 1 :y 2 :x 3)
  2. (list :x x :y y :z z :whole whole))
  3. ==> (:X 3 :Y 2 :Z 1 :WHOLE (:Z 1 :Y 2 :X 3))

You’ll use a **&whole** parameter in one of the macros that’s part of the HTML generation library you’ll develop in Chapter 31. However, I have a few more topics to cover before you can get to that. After two chapters on the rather Lispy topic of cons cells, you can now turn to the more prosaic matter of how to deal with files and filenames.


1It’s possible to build a chain of cons cells where the **CDR** of the last cons cell isn’t **NIL** but some other atom. This is called a dotted list because the last cons is a dotted pair.

2It may seem that the **NSUBST** family of functions can and in fact does modify the tree in place. However, there’s one edge case: when the “tree” passed is, in fact, an atom, it can’t be modified in place, so the result of **NSUBST** will be a different object than the argument: (nsubst 'x 'y 'y) X.

3**UNION** takes only one element from each list, but if either list contains duplicate elements, the result may also contain duplicates.

4It’s also possible to directly **SETF SYMBOL-PLIST**. However, that’s a bad idea, as different code may have added different properties to the symbol’s plist for different reasons. If one piece of code clobbers the symbol’s whole plist, it may break other code that added its own properties to the plist.

5Macro parameter lists do support one parameter type, **&environment** parameters, which **DESTRUCTURING-BIND** doesn’t. However, I didn’t discuss that parameter type in Chapter 8, and you don’t need to worry about it now either.

6When a **&whole** parameter is used in a macro parameter list, the form it’s bound to is the whole macro form, including the name of the macro.