Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expression that returns all currently scoped symbols in Clojure?

Assume the following,

(in-ns silly.fun)

(def a 1)

(defn fx [b]
  ((fn [c] (return-all-symbols)) (first b)))

I was wondering if it is possible to have a return-all-symbols function that would return the map of symbols/values currently scoped at its invocation. So, assuming the above was compiled and we were in the 'silly.fun namespace, we could run something like the following.

(fx [:hello]) => {"a" 1, "b" [:hello], "c" :hello}

I would like to use return-all-symbols for debugging purposes. Is return-all-symbols at all possible? If so, what is its implementation?

like image 219
Stephen Cagle Avatar asked Sep 19 '12 21:09

Stephen Cagle


2 Answers

It's possible, but as you've defined it you'd be pretty sad: you don't want a map with hundreds of entries referring to all the functions in clojure.core! And even if you only look in the current namespace, you forgot to include fx, which is a symbol whose value is a function. Plus there will often be lexical symbols you don't want, introduced by macros. eg, (let [[x y] foo]) would show four symbols available: foo, x, y, and something like vec__auto__4863.

Anyway, you probably have to live with some compromise over those issues, or else (and really I think this is better) specify which symbols you actually want a map of. But to automatically get values for those symbols which are either (a) lexical or (b) defined in the current namespace, and also (c) not mapping to a function, you could use:

(defmacro return-all-symbols []
  (let [globals (remove (comp :macro meta val) (ns-publics *ns*))
        syms (mapcat keys [globals, &env])
        entries (for [sym syms]
                  [`(quote ~sym) sym])]
    `(into {}
           (for [[sym# value#] [~@entries]
                 :when (not (fn? value#))]
             [sym# value#]))))


(def a 1)

(defn fx [b]
  ((fn [c] (return-all-symbols)) (first b)))

(fx [:hello])
;=> {a 1, c :hello, b [:hello]}
like image 59
amalloy Avatar answered Sep 20 '22 22:09

amalloy


Namespaces contain a map with all the currently scoped vars, which gives you part of what you want. It would miss lexicaly scoped symbols from expressions like (let [x 4] (return-all-symbols)) though may still be useful for debugging:

core> (take 2 (ns-map *ns*))
([sorted-map #'clojure.core/sorted-map] [read-line #'clojure.core/read-line])  

if you need more than this then you may need a real debugger that uses the java debugging interface. check out the clojure debugging toolkit

like image 2
Arthur Ulfeldt Avatar answered Sep 19 '22 22:09

Arthur Ulfeldt