Ответ 1
Макросы возвращают список, который затем оценивается в пространстве имен, из которого он вызывается, а не в пространстве имен, в котором он определен. Это отличается от функций, которые оцениваются в пространстве имен, в котором они определены. Это , потому что макросы возвращают выполняемый код, а не просто запускают его.
если я перехожу в другое пространство имен, например hello.core
и разворачиваю вызов на следующую дату, я получаю:
hello.core> (macroexpand-1 '(next-date weeks 2))
(weeks 2)
то после разложения недели разрешается с hello.core, в котором он, конечно, не определен. Чтобы исправить это, нам нужен возвращенный символ для переноса с ним информации о пространстве имен.
К счастью, вы можете явно разрешить символ в пространстве имен с ns-resolve
. Требуется пространство имен и символ и пытается найти его в пространстве имен, возвращающем нуль, если он не найден
(ns-resolve 'clj-time.core (symbol "weeks"))
#'clj-time.core/weeks
Далее ваш макрос примет символ и число, поэтому мы можем отказаться от явного вызова symbol
(ns-resolve 'clj-time.core 'weeks)
#'clj-time.core/weeks
теперь вам просто нужна функция, которая разрешает эту функцию, а затем создает список разрешенной функции, за которой следует номер,
(defmacro next-date [interval freq]
(list (ns-resolve 'clj-time.core interval) freq))
В вышеприведенном макросе все, что он делает, это вызов функции, который немедленно вызывается, поэтому вам даже не нужен макрос для этого:
(defn next-date [interval freq]
((ns-resolve 'clj-time.core interval) freq))
(next-date 'weeks 2)
#<Weeks P2W>
не-макро версия требует, чтобы вы процитировали интервал, потому что ему не нужно его оценивать, прежде чем вы сможете его найти. То, что макрос действительно покупает у вас здесь, - это необязательно включать цитату ценой необходимости требовать от всех вызывающих абонентов clj-time
конечно, вы могли бы просто потребовать clj-время повсюду, но это не совсем так.