This is another use for a macro like let/names
I provided in another answer. Here is a corrected version of it (you will need Tim Bradshaw's org.tfeb.hax.utilities
package for parse-simple-body
but this is just used to put declarations in the right place):
(defmacro let/names (bindings &body decls/forms)
(multiple-value-bind (decls forms) (parse-simple-body decls/forms)
(let ((<map> (make-symbol "MAP")))
`(let ,bindings
(let ((,<map> (list ,@(mapcar (lambda (b)
(let ((n (etypecase b
(symbol b)
(cons (car b)))))
`(cons ',n
(lambda (&optional (v nil vp))
(if vp
(setf ,n v)
,n)))))
bindings))))
,@decls
(flet ((valof (s)
(let ((m (assoc s ,<map>)))
(if m
(values (funcall (cdr m)) t)
(values nil nil))))
((setf valof) (n s)
(let ((m (assoc s ,<map>)))
(unless m
(error "~S unbound" s))
(funcall (cdr m) n))))
,@forms))))))
Now, for instance, you could write this:
(defun reset-variable (var val)
(let/names ((a 1) (b 2) (c 3))
(unless (nth-value 1 (valof var))
(error "~S not bound here" var))
(setf (valof var) val)
(values a b c)))
And now:
> (reset-variable 'a 15)
15
2
3
> (reset-variable 'd 14)
Error: d not bound here
1 (abort) Return to top loop level 0.
As you can see from the macro, all it does is build a secret table from the names of the variables to functions which access their bindings, and then defines a local valof
/ (setf valof)
function appropriately.