4

I would like to get the name of a var defined outside a function, from within a function. The name should be the name that I used at the scope of the original definition, not any nested bindings where I'm actually trying to use the name.

So I would like to do something like (academic example):

(defn f1 [x1] (println "hello, you passed var name >>" (get-var-name x1) "<<")
(defn f2 [x2] (f1 x2))
(defn f3 [x3] (let [zzz x3] (f2 zzz))
(def my-var 3.1414926)
(f3 my-var)
user> hello, you passed var name >>my-var<<

I'm able to do this macro based on some stuff i found:

(defmacro get-var-name [x]
  `(:name (meta (var ~x))))

This works when called eg from the REPL, but compiler chokes when called from an "inside" scope eg

(defn another-func [y]
  (get-var-name y))

Compiler says "saying Unable to resolve var y". (macroexpand...) shows it's trying to find local variable y in the current namespace, rather than the original variable in the current namespace. I think (var...) looks for namespace vars only, so this prevents the macro from working either within a function or another binding such as let.

I think I'm stuck having to manually get the variable name from the same scope where I define the variable and pass it along as an extra parameter. Is there a more elegant way to pass var name information through a chain of bindings to the point where it's used? That would be bad-ass.

thanks

Sonicsmooth
  • 2,673
  • 2
  • 22
  • 35

3 Answers3

8

It's not possible to get the name of the var used in an outside scope within a function - the function only receives a the value passed as a parameter at runtime, not the var itself.

The only thing you could potentially do is use macros instead of functions at each level. This allows you to pass the var itself through the different macros at compile time:

(defmacro f1 [x1] `(println "hello, you passed var name >>" ~(str x1) "<<"))
(defmacro f2 [x2] `(f1 ~x2))
(defmacro f3 [x3] (let [zzz x3] `(f2 ~zzz)))

(f3 my-var)
=> hello, you passed var name >> my-var <<

This is pretty ugly - you certainly don't want to be writing all of your code with macros just to get this feature! It might make sense though in some specialised circumstances, e.g. if you are creating some kind of macro-based DSL.

mikera
  • 105,238
  • 25
  • 256
  • 415
  • 4
    This, a hundred times this. The other answers (so far) go along with the questioner's assumption that vars are involved and actually work, which is not the case (as the last example in the question demonstrates). – amalloy Aug 08 '12 at 06:51
1

You can pass the actual var to the function rather than the var resolved value using #' reader macro as shown below:

user=> (defn f1 [x1] (println "hello, you passed var name >>" (:name (meta x1)) "<<"))
#'user/f1
user=> (defn f2 [x2] (f1 x2))
#'user/f2
user=> (defn f3 [x3] (let [zzz x3] (f2 zzz)))  
#'user/f3
user=> (def my-var 3.1414926)
#'user/my-var
user=> (f3 #'my-var)
hello, you passed var name >> my-var <<

In case you want the value bound to the var, you can use var-get function to do so.

Ankur
  • 33,367
  • 2
  • 46
  • 72
  • Thanks, this response actually triggered a multi-hour investigation by me into how symbols->vars->values work in clojure. It works from the repl with vars, but still doesn't work with let-bounded symbols. – Sonicsmooth Aug 08 '12 at 17:18
0

Why not just pass (get-var-name _symbol-here_) to functions where you will want to use the var name inside the body? For example, using exactly the same definitions of get-var-name, f2, f3, and my-var which you have given above (but changing f1 slightly):

(defmacro get-var-name [x]
  `(:name (meta (var ~x))))
(defn f1 [x1] (println "hello, you passed var name >>" x1 "<<"))
(defn f2 [x2] (f1 x2))
(defn f3 [x3] (let [zzz x3] (f2 zzz)))
(def my-var 3.1414926)
=> (f3 (get-var-name my-var))
hello, you passed var name >> my-var <<
=> nil

Maybe sometimes, you will also want to refer to the value that the symbol refers to in the function body. For example, let's say in f1 you want to also print out the value referenced by that var name. Here's how you could do that:

(defn f1 [x1] (println "hello, you passed var name >>" x1 "<<"
                       "\nwhich refers to value >>" @(resolve x1) "<<"))
=> (f3 (get-var-name my-var))
hello, you passed var name >> my-var << 
which refers to value >> 3.1414926 <<
=> nil

Note the @(resolve x1)--this is the thing returning the value that my-var refers to (and in turn my-var is the value referred to by x1).

Also, I want to mention that your current implementation of get-var-name will throw Exceptions when the argument passed to it is either not a symbol, or is a symbol, but is not currently bound to a value. Is this the behavior you want?

If what I propose does not answer your question, then it seems like you don't want to have to pass (get-var-name _symbol-here_) to the functions that may end up using the var name, but rather for some reason really want to be able to do the (get-var-name ...) from within the function body. If this is the case, why is it that you want to be able to do it that way? Or, if you feel I have not answered your question for some other reason, what is that reason?

Omri Bernstein
  • 1,893
  • 1
  • 13
  • 17
  • Thanks, but I need to get-var-name from within the function body. Basically I'm trying to append the name of the higher-scoped variable to some other text a few levels down in the function call. I think I can try this with a `(binding...)` before starting the call chain. – Sonicsmooth Aug 08 '12 at 17:08
  • @Sonicsmooth I think I see. I would be curious to see the real (i.e. non-academic-example) version of what you are doing/trying to do. – Omri Bernstein Aug 08 '12 at 17:19
  • ...ok I've given up on `(binding...)` too. It requires an additional dummy var in my namespace and makes it ugly. – Sonicsmooth Aug 08 '12 at 17:30
  • I'm trying to write my own database inner join, where each table is a set of clojure maps; within a set the maps share keys. (I know there is already a join in 'clojure.set). But if the two tables have a common key, one of the values gets clobbered during the (naive) first step of computing cartesian product, so I'm trying to keep the names of the keys distinct. Currently I just prepend "left." and "right." to shared key names, but I want to use the var names instead. I can pass the var used from REPL as in another response, but I want a let-bound symbol to be passed in also. – Sonicsmooth Aug 08 '12 at 17:40
  • @Sonicsmooth I understand more now, but honestly I'm not familiar with database operations, so I think I'm a little out of my depth. However, I think if you post a new question, describing this problem of "doing database inner join without losing shared keys" (or however you would title it), you may get a solution to your ultimate problem that may or may not involve getting var names from within nested scopes. – Omri Bernstein Aug 08 '12 at 18:28