Search code examples
functionclojuremetadatadefn

Retrieve Clojure function metadata dynamically


Environment: Clojure 1.4

I'm trying to pull function metadata dynamically from a vector of functions.

(defn #^{:tau-or-pi: :pi} funca "doc for func a" {:ans 42} [x] (* x x))
(defn #^{:tau-or-pi: :tau} funcb "doc for func b" {:ans 43} [x] (* x x x))

(def funcs [funca funcb])

Now, retrieving the metadata in the REPL is (somewhat) straight-forward:

user=>(:tau-or-pi (meta #'funca))
:pi

user=>(:ans (meta #'funca))
42

user=>(:tau-or-pi (meta #'funcb))
:tau

user=>(:ans (meta #'funcb))
43

However, when I try to do a map to get the :ans, :tau-or-pi, or basic :name from the metadata, I get the exception:

user=>(map #(meta #'%) funcs)
CompilerException java.lang.RuntimeException: Unable to resolve var: p1__1637# in this context, compiling:(NO_SOURCE_PATH:1) 

After doing some more searching, I got the following idea from a posting in 2009 (https://groups.google.com/forum/?fromgroups=#!topic/clojure/VyDM0YAzF4o):

user=>(map #(meta (resolve %)) funcs)
ClassCastException user$funca cannot be cast to clojure.lang.Symbol  clojure.core/ns-resolve (core.clj:3883)

I know that the defn macro (in Clojure 1.4) is putting the metadata on the Var in the def portion of the defn macro so that's why the simple (meta #'funca) is working, but is there a way to get the function metadata dynamically (like in the map example above)?

Maybe I'm missing something syntactically but if anyone could point me in the right direction or the right approach, that'd would be great.

Thanks.


Solution

  • Recently, I found it useful to attach metadata to the functions themselves rather than the vars as defn does.

    You can do this with good ol' def:

    (def funca ^{:tau-or-pi :pi} (fn [x] (* x x)))
    (def funcb ^{:tau-or-pi :tau} (fn [x] (* x x x)))
    

    Here, the metadata has been attached to the functions and then those metadata-laden functions are bound to the vars.

    The nice thing about this is that you no longer need to worry about vars when considering the metadata. Since the functions contain metadata instead, you can pull it from them directly.

    (def funcs [funca funcb])
    
    (map (comp :tau-or-pi meta) funcs) ; [:pi :tau]
    

    Obviously the syntax of def isn't quite as refined as defn for functions, so depending on your usage, you might be interested in re-implementing defn to attach metadata to the functions.