Подтвердить что ты не робот

Clojure defmacro теряет метаданные

Я пытаюсь создать небольшой макрос Clojure, который def a String с подсказкой типа:

(defmacro def-string [name value]
  `(def ^String ~name ~value))

(def-string db-host-option "db-host")

Когда я macroexpand он, подсказка типа теряется:

(macroexpand '(def-string db-host-option "db-host"))
;=> (def db-host-option "db-host")

Не обращайте внимания на мудрость типа, намекая на это.

Почему макрос теряет метаданные? Как написать этот макрос или любой, который включает метаданные?

4b9b3361

Ответ 1

^ - это макрос читателя. defmacro никогда не увидит его. Подсказка помещается в список (unquote name). Сравните, например, (meta ^String 'x) с (meta ' ^String x), чтобы увидеть эффект.

Вам нужно указать подсказку на символ.

(defmacro def-string
  [name value]
  `(def ~(vary-meta name assoc :tag `String) ~value))

И использование:

user=> (def-string foo "bar")
#'user/foo
user=> (meta #'foo)
{:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 5, :tag java.lang.String}

Ответ 2

Метаданные не отображаются в макроэкспонете, поскольку он должен быть "невидимым".

Если макрос верен (это не так), вы должны иметь возможность вызвать (meta # 'db-host-option), чтобы проверить метаданные на var.

Обратите внимание, что (def sym...) вставляет метаданные в var, которые он получает от символа. Но ^ Tag ~ name устанавливает метаданные в ~ имя (имя unquote), а не на переданный символ, привязанный к имени. Он ничего не может сделать с тех пор, как ^ Тег... обработка выполняется читателем, который уже завершен после начала макрорасширения.

Вы хотите что-то вроде

(defmacro def-string [name value]
  `(def ~(with-meta name {:tag String}) ~value))


user> (def-string bar 1)
#'user/bar
user> (meta #'bar)
{:ns #<Namespace user>, :name bar, :file "NO_SOURCE_FILE", :line 1, :tag java.lang.String}