如何动态查找Clojure函数的元数据?

时间:2011-04-08 08:14:45

标签: function clojure metadata clojure-contrib

说我有以下代码:

(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"}

有人可以向我解释一下吗?

4 个答案:

答案 0 :(得分:31)

功能 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 上的元数据。

答案 1 :(得分:19)

元数据附加到var,而不附加到函数。

因此,要获得图表标题,您必须从var的元数据中获取条目:graph-title。你觉得你的宏怎么样?

(defmacro get-graph-title
  [func]
  `(:graph-title (meta (var ~func))))

(get-graph-title func-1)
=> "Function 1"

答案 2 :(得分:2)

您在源代码中的符号 func-1上指定的元数据将被def特殊形式复制到名为func-1的 var 。请参阅http://clojure.org/special_forms

中的def文档

当你评估func-1这是一个与var绑定的符号时,你会得到var的(在这种情况下是函数对象)。见http://clojure.org/vars

函数对象本身不会自动接收符号/ var。

上手动指定的元数据

因此,您想要的信息根本不在函数中。它在var中,你必须指定你真的想要var func-1本身而不是它的值。这就是(var func-1)和等效的快捷方式#'func-1。

答案 3 :(得分:0)

虽然我不会说这是防弹的,但为了繁荣起见,我将其放在此处,以防万一出于某种原因,您只有Fn对象,而没有符号或Var。

据我所知,当defn求值时,它将生成一个名称为(munge fn-symbol)的类。有一个名为demunge的函数将为我们反向执行此操作。因此,从Fn对象的类名称中,我们可以找回符号,从符号中,我们可以找到var。

(-> (class some-fn)
    (print-str)
    (demunge)
    (symbol)
    (find-var)
    (meta))

很有趣的是,demungeclojure.main中都可以找到clojure.repl,它们的作用完全相同,因此,请要求其中的任何一个都带进去: / p>

(require '[clojure.main :refer (demunge)])
;; or
(require '[clojure.repl :refer (demunge)])

警告:

就像我之前说的,首先尝试做:

(meta #'some-fn)

与其说的是我的回答,不如说是因为它是适当的惯用机制。