Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying var or #' to a list of functions in Clojure

I'm trying to read metadata for a collection of functions in Clojure, but the var or reader special forms do not work unless they are directly dealing with the symbol.

; this works
(var my-fn)

; this doesn't
(defn val-it [x] (var x))
(val-it my-fn)

Is there any way to get this to work within the scope of another function?

like image 346
matsko Avatar asked Dec 26 '22 10:12

matsko


2 Answers

resolve returns the Var or class object corresponding the given symbol in the context of the current namespace. ns-resolve allows you to specify which namespace to resolve the symbol in.

(resolve 'my-fn)
;= #'some.ns/my-fn

If the symbol cannot be resolved to a Var, nil is returned.

like image 114
Michał Marczyk Avatar answered Jan 06 '23 01:01

Michał Marczyk


(var my-fn) does deal directly with the symbol because it is a special form (the reader receives the form unevaluated).

The metadata you want to read is stored in the var object, not in the function object.

Thus your goal, to read metadata from a list of function objects, is only achievable by traversing all existing vars and comparing their value by equality. I'd only recommend it if the function objects are the only way to start.

(defn meta-of
  "Returns a hashmap mapping the function objects in fn-objs to 
  a set of metadata of vars containing it."
  [fn-objs]
  (let [fn-objs (set fn-objs)]
    (reduce (fn [acc ns]
              (reduce (fn [acc var]
                        (let [val (var-get var)]
                          (cond-> acc
                                  (contains? fn-objs val)
                                  (update-in [val] (fnil conj #{}) (meta var)))))
                      acc
                      (vals (ns-interns ns)))) {} (all-ns))))
(def foo inc) ;; Now there are two vars that have the inc function as their value
(meta-of [inc])
{#<core$inc clojure.core$inc@66d7e31d>  ;; <- function object
 #{{:ns #<Namespace clojure.core>,  ;; <- metadata in clojure.core namespace
    :name inc,
    :file "clojure/core.clj",
    :column 1,
    :line 881,
    :arglists ([x]),
    :added "1.2",
    :inline #<core$inc__inliner clojure.core$inc__inliner@24f87069>,
    :doc
    "Returns a number one greater than num. Does not auto-promote\n  longs, will throw on overflow. See also: inc'"}
   {:ns #<Namespace user>,  ;; <- metadata in user namespace
    :name foo,
    :file "/tmp/form-init1078564431656334911.clj",
    :column 1,
    :line 1}}}
like image 26
Leon Grapenthin Avatar answered Jan 06 '23 00:01

Leon Grapenthin