Ответ содержится в вопросе: просто используйте gensym
, как если бы у Clojure не было автогензимов.
(defmacro make [v & body]
(let [value-sym (gensym)]
`(let [~value-sym ~(some-calc v)]
~@(replace {:value value-sym} body))))
Обратите внимание, что я не уверен, действительно ли вы хотите ~
или ~@
здесь - это зависит от того, предполагается ли body
последовательность выражений для выполнения в let
или последовательность аргументов для одного вызова функции.Но ~@
было бы намного более интуитивным / нормальным, так что я собираюсь догадаться.
Является ли этот макрос анафорическим, немного сомнительно: однозначно введение nv
в область вызова было,но это было в основном непреднамеренно, поэтому я бы сказал нет.В моей пересмотренной версии мы больше не представляем nv
или что-то подобное, но мы «волшебным образом» заменяем :value
на v
.Мы делаем это только на самом верхнем уровне тела, так что это не похоже на введение реальной области видимости - я бы сказал, что это больше похоже на неожиданное прерывание кода клиента в угловых случаях.
Для примеракак может появиться это обманчивое поведение, представьте, что одним из элементов body
является (inc :value)
.Он не будет заменен макросом и расширится до (inc :value)
, что никогда не удастся.Поэтому вместо этого я бы порекомендовал анафорический макрос real , который вводит реальную область действия для символа.Что-то вроде
(defmacro make [v & body]
`(let [~'the-value ~(some-calc v)]
~@body))
И тогда вызывающая сторона может просто использовать the-value
в своем коде, и она ведет себя так же, как реальное, регулярное локальное связывание: ваш макрос вводит его по волшебству, но не имеетлюбые другие специальные трюки.