Как динамически найти метаданные для функции Clojure?
Скажем, у меня есть следующий код:
(defn ^{:graph-title "Function 1"} func-1
[x]
(do-something-with x))
(defn get-graph-title
[func]
(str
((meta func) :graph-title)))
Я ожидаю, что это вернет "Function 1", но возвращает nil. Я думаю, что это связано с следующим различием, которое я не полностью понимаю:
(meta func-1)
=> {:ns some-ns-info, :name func-1}
(meta #'func-1)
=> {:ns some-ns-info, :name func-1, :graph-title "Function 1"}
Может кто-нибудь объяснить это мне?
Ответы
Ответ 1
Метаданные привязаны к var, а не к функции.
Таким образом, чтобы получить заголовок графа, вы должны получить запись :graph-title
из мета файла var. Как вам нравятся ваши макросы?
(defmacro get-graph-title
[func]
`(:graph-title (meta (var ~func))))
(get-graph-title func-1)
=> "Function 1"
Ответ 2
Есть метаданные по функции func-1
, метаданные на Var #'func-1
и метаданные на символе 'func-1
. Макрос чтения Clojure ^
добавляет метаданные к символу во время чтения. Макрос defn
копирует метаданные из символа в Var во время компиляции.
До Clojure 1.2 функции не поддерживали метаданные. В Clojure 1.2 они делают, и defn
также копирует некоторые стандартные метаданные Var в функцию:
Clojure 1.2.0
user=> (defn ^{:foo :bar} func-1 [] nil)
#'user/func-1
user=> (meta func-1)
{:ns #<Namespace user>, :name func-1}
user=> (meta #'func-1)
{:foo :bar, :ns #<Namespace user>, :name func-1, ...
Однако в текущих Clojure 1.3 моментальных снимках defn
не копирует никакие метаданные функции:
Clojure 1.3.0-master-SNAPSHOT
user=> (defn ^{:foo :bar} func-1 [] nil)
#'user/func-1
user=> (meta func-1)
nil
user=> (meta #'func-1)
{:foo :bar, :ns #<Namespace user>, :name func-1, ...
В общем, если вы хотите попасть в метаданные определения, вам нужны метаданные в Var.
Ответ 3
Метаданные, указанные вами на символе func-1 в исходном коде, копируются в var с именем func-1 специальной формой def. См. Документацию для def в http://clojure.org/special_forms
Когда вы оцениваете func-1
, где символ, привязанный к var, вы получаете значение var (которое в этом случае является объектом функции). См. http://clojure.org/vars
Сам объект функции автоматически не получает метаданные, указанные вручную в символе /var.
Итак, информация, которую вы хотите, вообще не входит в функцию. Это в var, и вы должны указать, что вам действительно нужен сам var func-1
, а не его значение. Это то, что (var func-1), и эквивалентный short-cut # 'func-1 делает.