General case
The answer is the same for setf
functions and regular functions.
Let's say you have another function f
that you want to call to print all the values in a list:
(defun f (list)
(mapc 'print list))
You can rewrite it recursively, you have to consider the two distinct case of recursion for a list, either it is nil or a cons cell:
(defun f (list)
(etypecase list
(null ...)
(cons ...)))
Typically in the null
case (this is a type), you won't do anything.
In the general cons
case (this is also a type), you have to process the first item and recurse:
(defun f (list)
(etypecase list
(null nil)
(cons
(print (first list))
(f (rest list)))))
The call to f
is in tail position: its return value is the return value of the enclosing f
, no other processing is done to the return value.
You can do the same with your function.
Note
It looks like the setf
function defined in the book does not return the value being set (the color), which is bad practice as far as I know:
all that is guaranteed is that the expansion is an update form that works for that particular implementation, that the left-to-right evaluation of subforms is preserved, and that the ultimate result of evaluating setf is the value or values being stored.
5.1.1 Overview of Places and Generalized Reference
Also, in your specific case you are subject to 5.1.2.9 Other Compound Forms as Places, which also says:
A function named (setf f)
must return its first argument as its only value in order to preserve the semantics of setf
.
In other words (setf uniform-color)
should return color
.
But apart from that, the same section guarantees that a call to (setf (uniform-color ...) ...)
expands into a call to the function named (setf uniform-color)
, so it can be a recursive function too. This could have been a problem if this was implemented as macro that expands into the body of your function, but fortunately this is not the case.
Implementation
Setting all the colors in a list named marbles
to "yellow"
is done as follows:
(setf (uniform-color marbles) "yellow")
You can define (setf uniform-color)
recursively by first setting the color of the first marble and then setting the color of the rest of the marbles.
A possible tail-recursive implementation that respects the semantics of setf
is:
(defun (setf uniform-color) (color list)
(if list
(destructuring-bind (head . tail) list
(setf (marble-color head) color)
(setf (uniform-color tail) color))
color))