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

Помогите мне написать макрос Clojure, который автоматически добавляет метаданные к определению функции

Я понимаю, что первым правилом Macro Club является "Не использовать макросы", поэтому следующий вопрос больше рассматривается как упражнение в изучении Clojure, чем что-либо еще (я понимаю, что это не обязательно лучшее использование макросов).

Я хочу написать простой макрос, который действует как обертка вокруг обычного макроса (defn) и завершает добавление некоторых метаданных к определенной функции. Поэтому я хотел бы иметь что-то вроде этого:

(defn-plus f [x] (inc x))

... разверните что-то вроде этого:

(defn #^{:special-metadata :fixed-value} f [x] (inc x))

В принципе, мне это не кажется трудным, но у меня возникают проблемы с приведением в соответствие специфики правильной обработки [args] и других форм в определенной функции.

В качестве бонуса, если возможно, я хотел бы, чтобы макрос мог обрабатывать все разрозненные формы defn (т.е. с или без докстеров, множественных определений arity и т.д.). Я видел некоторые вещи в пакете clojure-contrib/def, который выглядел, возможно, полезным, но было сложно найти пример кода, который их использовал.

4b9b3361

Ответ 1

Обновлено:

Предыдущая версия моего ответа была не очень надежной. Это похоже на более простой и правильный способ сделать это, украденное с clojure.contrib.def:

(defmacro defn-plus [name & syms]
  `(defn ~(vary-meta name assoc :some-key :some-value) [email protected]))

user> (defn-plus ^Integer f "Docstring goes here" [x] (inc x))
#'user/f
user> (meta #'f)
{:ns #<Namespace user>, :name f, :file "NO_SOURCE_PATH", :line 1, :arglists ([x]), :doc "Docstring goes here", :some-key :some-value, :tag java.lang.Integer}

#^{} и with-meta - это не одно и то же. Для объяснения различия между ними см. Раздел "Богатое обсуждение" Clojure список рассылки. Все это немного сбивает с толку, и в списке рассылки появилось множество раз; см. также здесь.

Обратите внимание, что def является специальной формой и обрабатывает метаданные немного странно по сравнению с некоторыми другими частями языка. Он устанавливает метаданные var you def fing в метаданные символа, который называет var; что единственная причина, по которой это работает, я думаю. См. Класс DefExpr в Compiler.java в источнике Clojure, если вы хотите увидеть все это.

Наконец, страница 216 Программирование Clojure говорит:

Обычно вы должны избегать макросов чтения в макрорасширениях, поскольку макросы читателя оцениваются во время чтения до начала макрораспределения.