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).