Я изучаю макросы в clojure. Мне нужна помощь с разрешением символов в макросах.
(ns macros.testing)
(def no (rand-int 10))
(defmacro drawer []
`(do
~@(for [i (range no)]
`(print ~i))))
(drawer) ;; This can resolve the global variable `no` properly
(defmacro drawer-2 [n]
`(do
~@(for [i (range n)]
`(print ~i))))
(drawer-2 10) ;; This works fine too
(drawer-2 n) ;; This is not working, I'm unable to pass the global variable as an argument to the macro, although it's visible in the other case
Сообщение об ошибке class clojure.lang.Symbol cannot be cast to class java.lang.Number (clojure.lang.Symbol is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap').
Почему clojure не может разрешить глобальную переменную во втором случае и почему он может сделать это для первого макроса?





Когда макрос оценивается, его аргументы передаются «как есть», без разрешения символов, как если бы все они были заключены в кавычки. Поэтому, если вы передадите n, внутри макроса это будет символ n, а не значение, содержащееся в соответствующей переменной. Если пройдете (inc 10), будет '(inc 10). И так далее.
Итак, в drawer(range no) работает, потому что no относится к переменной, связанной с результатом (rand-int 10).
В drawer-2 аргумент n имеет значение (symbol "n") (или просто 'n) во время вычисления макроса. И (range 'n) не может работать — это как раз то, о чем вы видели исключение «не может преобразовать символ в число».
Чтобы исправить это, вы можете явно разрешить переданный символ, используя resolve (это @ необходимо для получения значения разрешенной переменной):
(def value 10)
(defmacro drawer-2 [n]
(let [n (if (symbol? n)
@(resolve n)
n)]
`(do
~@(for [i (range n)]
`(print ~i)))))
(drawer-2 value)
См. также этот ответ: stackoverflow.com/questions/60212576/…