Запутанность пространства имен и макросы - PullRequest
10 голосов
/ 13 марта 2012

Я хочу написать макрос, который использует функции из библиотеки clj-time. В одном пространстве имен я хотел бы назвать макрос следующим образом:

(ns budget.account
  (:require [budget.time]))

(budget.time/next-date interval frequency)

Макрос следующей даты будет определен в другом файле, например:

(ns budget.time
  (:require [clj-time.core :as date]))

(defmacro next-date [interval freq]
  `(~interval ~freq))

Если макрос вызывался со следующими аргументами (budget.time / next-date interval freq) и интервалом и freq, где «недели» и «2» соответственно, то расширение макроса выглядело бы примерно так (clj-time. ядро / недели 2)

Всякий раз, когда я пытаюсь сделать это из REPL, он не может разрешить пространство имен.

Есть ли способ заставить макрос разрешить интервал для аргументов в пространстве имен clj-time? Каков наилучший способ сделать это?

Спасибо!

1 Ответ

13 голосов
/ 13 марта 2012

Макросы возвращают список, который затем оценивается в пространстве имен, из которого он вызывается, а не в пространстве имен, в котором он определен. Это отличается от функций, которые оценивают в пространстве имен, в котором они были определены.Это , потому что макросы возвращают код для запуска, а не просто запускают его.

, если я иду в другое пространство имен, например hello.core, и расширяю вызов до следующей даты.get:

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-time везде, но это не совсем так.точка.

...