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

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

`(let [x# 1]
   ~@(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]
     ~@(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 с вложенными синтаксическими кавычками и заставить их создавать один и тот же символ?

3 ответа

Решение

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

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

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

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

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

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

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

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

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

 `(let [x# 1]
   (doto x#
     ~@(repeat 3 `println)))

В более общем случае, когда вы столкнетесь с такой ситуацией, вы можете сделать следующее:

      (let [x `x#]
   `(let [~x 1]
      ~@(repeat 3
                `(println ~x))))

Чтобы было ясно, вы создаете auto-gensym и привязываете его вне формы с синтаксическими кавычками, а затем внутри любых вложенных форм, которые требуют этого, вы можете просто использовать syntax-unquote.

Другие вопросы по тегам