Координирование автоматического 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)))