Search code examples
common-lispdynamic-binding

Shadowing parts of a dynamic variable


Maybe I'm trying to be too creative here.

Is it possible to extend the concept of dynamic binding to SETFable places of a dynamic variable, so that I can shadow part of a dynamic variable (such as a plist) with LET bindings?

For example, I would like to be able to do something like:

(defparameter *foo* '(:one 1))

(let (((getf *foo* :one) 2))
  (do-things))

To shadow the value of :one with 2.

This example doesn't work since (getf *foo* :one) is not a variable name that LET can assign a value to, but perhaps there is another way?


Solution

  • There is no standard way, but some implementations may offer extensions, e.g., letf.

    Alternatively, you can use unwind-protect yourself:

    (let ((old-value (getf *foo* :one)))
      (unwind-protect
           (progn (setf (getf *foo* :one) 2)
                  (do-things))
        (setf (getf *foo* :one) old-value)))
    

    If this is a common operation in your code, you might even define a macro for that:

    (defmacro with-one (tmp-one &body body)
      "Bind (getf *foo* :one) to tmp-one around body."
      (let ((old-value (gensym "WITH-ONE-OLD")))
        `(let ((,old-value (getf *foo* :one)))
           (unwind-protect
                (progn (setf (getf *foo* :one) ,tmp-one)
                       ,@body)
             (setf (getf *foo* :one) ,old-value)))))