Координирование автоматического gensym во вложенных синтаксических котировках в Clojure

В Clojure вам нужно использовать gensym для создания символов для внутреннего использования в ваших макросах, чтобы поддерживать их гигиеничность. Однако иногда вам нужно использовать один и тот же символ во вложенных синтаксических кавычках. Например, если я хочу привязать значение к символу с помощью let и напечатать его три раза в развернутом цикле, я бы сделал

`(let [x# 1]
   [email protected](repeat 3
             `(println x#)))

Но это создаст

(clojure.core/let [x__2__auto__ 1]
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__)
                  (clojure.core/println x__1__auto__))

x# генерирует другой символ в форме let, чем в формах println, вложенных в него, потому что они были созданы из разных синтаксических кавычек.

Чтобы решить эту проблему, я могу заранее создать символ и ввести его в синтаксические кавычки:

(let [x (gensym)]
  `(let [~x 1]
     [email protected](repeat 3
               `(println ~x)))
) 

Это приведет к правильному результату с тем же символом, который необходим везде:

(clojure.core/let [G__7 1]
                  (clojure.core/println G__7)
                  (clojure.core/println G__7)
                  (clojure.core/println G__7))

Теперь, когда он дает правильный результат, сам код выглядит уродливым и подробным. Мне не нравится, когда нужно "объявить" символ, и синтаксис инъекции делает его похожим на то, что он появился вне макроса или вычислялся где-то внутри него. Я хочу иметь возможность использовать синтаксис auto-gensym, который дает понять, что это макро-внутренние символы.

Итак, есть ли способ использовать auto-gensym с вложенными синтаксическими кавычками и заставить их выдавать один и тот же символ?

Ответы

Ответ 1

Символы Auto-gensym'd действительны только в цитате синтаксиса, которая их определяет, и они не работают в некотируемом коде, потому что это не является частью цитаты синтаксиса.

Здесь символ x# заменяется на него gensym, потому что он входит в область цитаты синтаксиса:

core> `(let [x# 1] x#)
(clojure.core/let [x__1942__auto__ 1] x__1942__auto__)

И если вы его не цитируете, он больше не будет переведен в цитату синтаксиса:

core> `(let [x# 1] [email protected]#)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: x# in this context, compiling:(NO_SOURCE_PATH:1) 

Авто-gensyms - очень удобный ярлык в цитате синтаксиса, везде, где вам, по-видимому, нужно использовать gensym напрямую, как в случае с вашим более поздним примером.

существуют другие способы структурирования этого макроса, так что автогенемы будут работать, хотя объявление gensymed символов в let в верхней части макроса очень нормально в Clojure и других lisps.

Ответ 2

Ваш метод (вызов gensym) является правильным.

Однако в некоторых случаях вы можете использовать умное использование doto, -> или ->>. См:

 `(let [x# 1]
   (doto x#
     [email protected](repeat 3 `println)))