How do I get &env data in CLJS-macros?


#1

In clj/cljr I use the following to get all locally defined variables:

(defmacro get-env
  []
  (into {} (for [k (keys &env)]
             [`'~k k])))

E.g. (do (defn a [x] (get-env)) (a 5)) would show me that x is defined to 5.

When trying the same macro in cljs though, I get this:

{:protocol-inline :protocol-inline, :fn-scope :fn-scope, :locals :locals, :repl-env :repl-env, :ns :ns, :def-emits-var :def-emits-var, :protocol-impl :protocol-impl, :column :column, :root-source-info :root-source-info, :line :line, :context :context}

Is there a way in cljs to extract the locally defined variables?


#2

You have to look at :locals in &env.


#3

How? I tried doing (:locals &env) or similar without success. :slight_smile:


#4

:locals is a map from symbol to binding info, which is yet another map. You cannot write the same code you would for Clojure. For details of what’s in the binding info map, I suggest reading the ClojureScript analyzer source - it’s not as scary as it sounds. Eventually we may have specs for this stuff.


#5

I’m having a hard time understanding CLJS overall. I tried looking inside (:locals &env), (in a clj file I have: (defmacro get-env2 [] (:locals &env)) and I get data back, but it seems like sometimes I get correct data:

cljs-example.core=> (defn a [b] (s/get-env2))
{b {:name b, :binding-form? true, :op :var, :env {:context :expr, :line 1, :column 10}, :column 10, :line 1, :info {:name b, :shadow nil}, :tag nil, :shadow nil}}

But sometimes something else happens:

cljs-example.core=> (defn a [b] (s/get-env2))
#'cljs-example.core/a
cljs-example.core=> (a 5)
{5 {:name 5, :binding-form? true, :op :var, :env {:context :expr, :line 1, :column 10}, :column 10, :line 1, :info {:name 5, :shadow nil}, :tag nil, :shadow nil}}

I understand if I sound whiny, but I feel like I run into weird behaviour all the time using CLJS… :frowning: I just don’t get it.


#6

Your macro expands to code that will be evaluated. So you see the b symbol getting replaced my the value you passed as b, so 5.

(defmacro get-env2 []
  `(quote ~(:locals &env)))

This should work if you just want the pure data.


#7

Thank you! That makes sense! I wonder how I managed to sometimes get data, and sometimes code… :slight_smile: Oh well.