Paso del estado de tiempo de compilación entre macros anidadas en Clojure

Estoy tratando de escribir una macro que se pueda usar tanto de forma global como anidada, así:

;;; global:
(do-stuff 1)

;;; nested, within a "with-context" block:
(with-context {:foo :bar}
  (do-stuff 2)
  (do-stuff 3))

Cuando se usa de forma anidada,do-stuff debería tener acceso a{:foo :bar} establecido porwith-context.

He podido implementarlo así:

(def ^:dynamic *ctx* nil)

(defmacro with-context [ctx & body]
  `(binding [*ctx* ~ctx]
     (do ~@body)))

(defmacro do-stuff [v]
  `(if *ctx*
     (println "within context" *ctx* ":" ~v)
     (println "no context:" ~v)))

Sin embargo, he estado tratando de cambiar elif dentrodo-stuff del tiempo de ejecución al tiempo de compilación, porque sido-stuff se llama desde dentro del cuerpo dewith-context o globalmente es una información que ya está disponible en tiempo de compilación.

Desafortunadamente, no he podido encontrar una solución, porque las macros anidadas parecen expandirse en múltiples "ejecuciones de expansión de macro", por lo que el enlace dinámico de*ctx* (como se establece dentro dewith-context) ya no está disponible cuandodo-stuff se expande Entonces esto no funciona:

(def ^:dynamic *ctx* nil)

(defmacro with-context [ctx & body]
  (binding [*ctx* ctx]
    `(do ~@body)))

(defmacro do-stuff [v]
  (if *ctx*
    `(println "within context" ~*ctx* ":" ~v)
    `(println "no context:" ~v)))

¿Alguna idea de cómo lograr esto?

¿O mi enfoque es totalmente loco y hay un patrón sobre cómo pasar el estado de una macro a una anidada?

EDITAR:

El cuerpo dewith-context debería poder trabajar con expresiones arbitrarias, no solo condo-stuff (u otras funciones contextuales / macros). Entonces algo como esto también debería ser posible:

(with-context {:foo :bar}
  (do-stuff 2)
  (some-arbitrary-function)
  (do-stuff 3))

(Soy consciente de quesome-arbitrary-function se trata de efectos secundarios, podría escribir algo en una base de datos, por ejemplo).

Respuestas a la pregunta(3)

Su respuesta a la pregunta