Передача состояния времени компиляции между вложенными макросами в Clojure
Я пытаюсь написать макрос, который можно использовать как в глобальном, так и во вложенном виде, например:
;;; global:
(do-stuff 1)
;;; nested, within a "with-context" block:
(with-context {:foo :bar}
(do-stuff 2)
(do-stuff 3))
Когда используется вложенным способом,do-stuff
должен иметь доступ к{:foo :bar}
установить с помощьюwith-context
.
Я смог реализовать это так:
(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)))
Тем не менее, я пытался сдвинутьif
вdo-stuff
от времени выполнения до времени компиляции, потому чтоdo-stuff
вызывается изнутри телаwith-context
или глобально это информация, которая уже доступна во время компиляции.
К сожалению, я не смог найти решение, потому что вложенные макросы, кажется, расширяются в несколько «запусков макроса», поэтому динамическое связывание*ctx*
(как установлено вwith-context
) больше не доступен, когдаdo-stuff
расширяется. Так что это не работает:
(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)))
Есть идеи, как это сделать?
Или мой подход совершенно безумный, и есть шаблон, как передать состояние таким способом от одного макроса к вложенному?
РЕДАКТИРОВАТЬ:
Телоwith-context
должен уметь работать с произвольными выражениями, а не только сdo-stuff
(или другие контекстно-зависимые функции / макросы). Так что подобное должно быть возможно:
(with-context {:foo :bar}
(do-stuff 2)
(some-arbitrary-function)
(do-stuff 3))
(Я знаю, чтоsome-arbitrary-function
о побочных эффектах, это может написать что-то в базу данных, например.)