Passando o estado de tempo de compilação entre macros aninhadas no Clojure
Estou tentando escrever uma macro que pode ser usada de maneira global e aninhada, assim:
;;; global:
(do-stuff 1)
;;; nested, within a "with-context" block:
(with-context {:foo :bar}
(do-stuff 2)
(do-stuff 3))
Quando usado da maneira aninhada,do-stuff
deve ter acesso a{:foo :bar}
definido pelawith-context
.
Eu fui capaz de implementá-lo assim:
(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)))
No entanto, tenho tentado mudar oif
dentrodo-stuff
do tempo de execução para o tempo de compilação, porque sedo-stuff
está sendo chamado de dentro do corpo dewith-context
ou globalmente é uma informação que já está disponível no momento da compilação.
Infelizmente, não consegui encontrar uma solução, porque as macros aninhadas parecem se expandir em várias "execuções de expansão de macro", portanto, a ligação dinâmica de*ctx*
(conforme definido emwith-context
) não está mais disponível quandodo-stuff
fica expandido. Portanto, isso não 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)))
Alguma idéia de como fazer isso?
Ou minha abordagem é totalmente insana e há um padrão de como passar o estado de uma maneira para uma macro para uma aninhada?
EDITAR:
O corpo dewith-context
deve poder trabalhar com expressões arbitrárias, não apenas comdo-stuff
(ou outras funções / macros com reconhecimento de contexto). Portanto, algo como isso também deve ser possível:
(with-context {:foo :bar}
(do-stuff 2)
(some-arbitrary-function)
(do-stuff 3))
(Eu sei quesome-arbitrary-function
trata de efeitos colaterais, pode escrever algo em um banco de dados, por exemplo.)