Координация авто-гензима во вложенных синтаксических кавычках в 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.