Как динамически найти метаданные для функции 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 делает.