Передача состояния времени компиляции между вложенными макросами в 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 о побочных эффектах, это может написать что-то в базу данных, например.)

Ответы на вопрос(3)

Ваш ответ на вопрос