Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get name of argument in Clojure?

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

like image 806
Sonicsmooth Avatar asked Oct 21 '25 01:10

Sonicsmooth


2 Answers

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.

like image 116
mikera Avatar answered Oct 24 '25 06:10

mikera


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.

like image 39
Ankur Avatar answered Oct 24 '25 04:10

Ankur



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!