Note: This answer is not a complete solution, as it does not handle the case where the key is not found. It is simply to point you in the right direction.
There are a couple of layers to this question. First off, push
is a macro that more or less expands to a setf
call. Thus, we need to define a setf
expansion for getf-string-equal
. That's fairly easy to do with one little complication.
(defsetf getf-string-equal (plist indicator) (value)
(let ((i (gensym))
(rest (gensym))
(match (gensym)))
`(let ((,match (loop
for (,i . ,rest) on ,plist by #'cddr
when (string-equal ,i ,indicator)
return ,rest)))
(if ,match
(setf (car ,match) ,value)
nil))))
Let's break that down. We're using the long form of defsetf
to define an expansion for our function. plist
and indicator
are the arguments, same as before, and value
is a new value to assign. But this isn't a function; it's closer to a macro. So we're going to generate some code. To that end, we need some gensym
calls to get some temporary variables.
Next, we generate code that runs the same loop that you used in your accessor. The difference here is that, rather than returning the actual value, we're going to return the cons cell that encloses it. That way, we can set the car
in that cell and have the effect actually modify the data structure. If the match is found, we do just that, and we're done.
However, the problem arises if the match isn't found. If there is no such cons
cell, we can't possibly set its car
. That's the second case in this if
. We'd like to simply expand to a `(setf ,plist ,value)
, which would have the correct semantics. However, defsetf
won't let us have access to the actual variable used like a macro would. Thus, to fully solve this problem, including the corner case where the value is not found, we would have to dip into define-setf-expander
, the completely general form of defsetf
. To give you a taste of the generality of this macro, to correctly write a setf
expansion using it, your expansion must be an expression which returns five different values.