+(defmacro setf-default (&rest specs &environment env)
+ "Like setf, but only sets places which are currently nil.
+
+ The arguments are an alternating list of PLACEs and DEFAULTs. If a PLACE
+ is nil, the DEFAULT is evaluated and stored in the PLACE; otherwise the
+ default is /not/ stored. The result is the (new) value of the last
+ PLACE."
+ (labels ((doit (specs)
+ (cond ((null specs) nil)
+ ((null (cdr specs))
+ (error "Odd number of arguments for SETF-DEFAULT."))
+ (t
+ (let ((place (car specs))
+ (default (cadr specs))
+ (rest (cddr specs)))
+ (multiple-value-bind
+ (vars vals store-vals writer reader)
+ (get-setf-expansion place env)
+ `(let* ,(mapcar #'list vars vals)
+ (or ,reader
+ (multiple-value-bind ,store-vals ,default
+ ,writer))
+ ,@(and rest (list (doit rest))))))))))
+ (doit specs)))
+