Макрос Clojure - определяет привязку, имя которой состоит из аргумента - PullRequest
3 голосов
/ 26 ноября 2010

ОК, я хочу написать макрос Clojure, который определяет struct-map и позволяет вызывающей стороне указывать типы для каждого поля.

Подпись будет выглядеть так:

(defmodel category :id Integer :name String)

Это создает struct-map категорию, а также создает привязку *category-meta*, которая является картой {:id Integer :name String}

Вот мой макрос для достижения этой цели:

(defmacro defmodel [name & field-spec]
    `(let [fields# (take-nth 2 ~@field-spec)]
        (defstruct ~name fields#)
        (def *~name-meta* (reduce #(assoc %1 (first %2) (last %2))) (partition 2 ~@field-spec))))

Однако проблема в том, что я не могу определить привязку, имя которой состоит из другого имени. По сути, (def *~name-meta* ...) не работает.

Как мне этого добиться?

Спасибо.

1 Ответ

4 голосов
/ 27 ноября 2010

(Обновлено с отладочной версией макроса из текста вопроса.)

Это должно работать как указано:

(defmacro defmodel [name & field-spec]
  `(do (defstruct ~name ~@(take-nth 2 field-spec))
       (def ~(symbol (str "*" name "-meta*"))
         (reduce #(assoc %1 (first %2) (last %2))
                 {}
                 (partition 2 '~field-spec)))))

Ответ на главнуювопрос в том, чтобы использовать ~(symbol (str "*" name "-meta*")) вместо *~name-meta*.~ снимает в кавычки следующее выражение в синтаксической кавычке, вставляя его возвращаемое значение в соответствующее место данной структуры списка.

Необходимы были некоторые другие модификации - в частности, defstruct требует, чтобыключи должны быть переданы ему как отдельные аргументы, а не как один seq (или имя переменной, содержащей такой seq), reduce для работы здесь требуется явное начальное значение и т. д.

Кстати, если только вынужно придерживаться Clojure 1.1, вы можете использовать 1.2 * defrecord вместо defstruct - на самом деле, последний устарел в 1.2.

...