how to get the name string from a variable or function? - clojure

I would like to get the name of a variable as a string in Clojure.
However, unlike in this question, I would get the name of the original binding (not sure if the terminology is correct, so please see my example below).
(def sample-data1 {:data 1})
(def sample-data2 {:data 2})
(def sample-data3 {:data 3})
(doseq
[my-var [sample-data1 sample-data2 sample-data3]]
(println (name 'my-var)))
In this case, instead of printing
my-var
my-var
my-var
I would like to have :
sample-data1
sample-data2
sample-data3
How do I do that in Clojure ?
Note : This is probably not the prettiest thing to do in a real app. But my question is about if this is this possible.
To clarify : this is for prototyping purposes. In this specific case instead of printing I am filling a (postgres) database with fake data that I write by hand in variables, and I'd like to track where my variable end up without writing too much code.

def creates a var with the same name as the name of the symbol supplied as def first parameter. Var in clojure differs from what we usually meant by variable whereas a variable usually can be visualized as a box that contains a value and a var is something that points to a value.
From your example, (here, I am in user namespace)
user> (def sample-data1 {:data 1})
#'user/sample-data1
clojure creates a #'user/sample-data1 var in user namespace which points to {:data 1} value. You can get the value back from that newly create var by deref-ing it using deref or # shorthand.
user> (deref #'user/sample-data1)
{:data 1}
user> ##'user/sample-data1
{:data 1}
or if you evaluate a symbol, clojure looks up for a var in the current namespace and returns the value pointed by that var.
user> sample-data1
{:data 1}
you can get a var from a symbol by using var for #' shorthand.
user> (var sample-data1)
#'user/sample-data1
user> #'sample-data1
#'user/sample-data1
So, you want the name of the var. The name of the var is contained inside the var's meta data which can be accessed using meta applied to a var.
user> (meta #'sample-data1)
{:line 1, :column 1, :file "/tmp/form-init3246148490151701909.clj",
:name sample-data1, :ns #object[clojure.lang.Namespace 0x39ba2c26 "user"]}
Above, you can see that the name of the var is located by :name key.
so, you can get the name of a var from its metadata.
user=> (:name (meta #'sample-data1))
sample-data1
I prefer not to have too many nested parenthesis, so I use thread-first macro ->
user=> (-> #'sample-data1 meta :name)
sample-data1
the answer
So back to your question, you need the sample-data[1-3] vars and their metadata to get their name.
(def sample-data1 {:data 1})
(def sample-data2 {:data 2})
(def sample-data3 {:data 3})
(doseq
[my-var [#'sample-data1 (var sample-data2) #'sample-data3]]
(println (-> my-var meta :name)))
and the result is
sample-data1
sample-data2
sample-data3
nil

Related

Clojure function self reference to get own metadata

When I attach some metadata to a function and then call it I am not able to access those metadata within that function
(let [I (fn I [x] (println I) (println (meta I)))]
(let [f (with-meta I {:rr 5})]
(println I)
(println f)
(f I)))
I see that the self reference from within the function is not the function instance actually invoked and thus no metadata is available through that self reference. I need the self reference to give me the function instance actually invoked to access those metadata
I think that the problem is that your conflating the value of the function and the identity of the function together. It's a thing many other languages do so it's natural when you're learning Clojure. In your example, I has a reference to itself, and looks up the metadata from that reference, which returns nil. You then create f which is the same as I, but with some metadata. So when you run f it looks up the metadata on I and returns nil. Defining f doesn't change I at all, it just creates a new thing in terms of the old thing. If you want to change something you need to introduce a reference type that you can change. There are several of these, but usually to store functions you'd use a Var (see here for reference)
(defn i [] (meta i))
(i) ;;=> nil
(alter-var-root #'i with-meta {:rr 5})
(i) ;;=> {:rr 5}
Here we define a function in the current namespace called i which just returns it's own metadata. We call it to get nil. Then we alter the global reference with some new metadata, and call it again.
If you wanted a more lexically scoped example, you could use an atom as below:
(let [i (atom nil)
f (fn [] (meta #i))]
(reset! i f)
(prn 'before '>> (#i))
(swap! i with-meta {:rr 5})
(prn 'after '>> (#i)))
However, other than learning how these things fit together, I'm not sure what the goal is. It's probably a bad idea to try and use these structures in a real program that you plan on maintaining.
Rather accidentally, I found a trick that enables functions to read it own metadata. It appears, the Clojure compiler generates metadata support code differently when the original function definition has custom metadata. If it is present, (meta fn-name) works inside the body of the function, otherwise it does not. For example, the following produces the result desired by the OP:
*clojure-version*
;;=> {:major 1, :minor 10, :incremental 0, :qualifier nil}
(let [f1 ^{:foo true} (fn f [] (meta f))
f2 (with-meta f1 {:bar true})]
(prn (f1))
(prn (f2)))
;;=> {:foo true}
;;=> {:bar true}
;;=> nil
We can examine the code generated for a function without the metadata in the original definition - there is just the invoke method
(require '[clojure.pprint :as p])
(let [ff (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x2b56b137 "public java.lang.Object user$eval2171$f__2172.invoke()"])
;;=> nil
And when the metadata is present, additional methods (meta and withMeta) are generated to deal with the metadata.
(let [ff ^:foo (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x3983bd83 "public clojure.lang.IObj user$eval2175$f__2176.withMeta(clojure.lang.IPersistentMap)"]
;;=> #object[java.lang.reflect.Method 0x547d182d "public clojure.lang.IPersistentMap user$eval2175$f__2176.meta()"]
;;=> #object[java.lang.reflect.Method 0x62c3d0fe "public java.lang.Object user$eval2175$f__2176.invoke()"])
;;=> nil
Welcome to Clojure, #xstreamer!
I'm going to suggest something different from what (precisely) you're asking for. I don't know how querying the function's metadata from within the function should work, really. So I'm going to suggest defining the function first, and redefining the function metadata afterwards. This is fairly simple in Clojure.
(defn f
"Boring doc"
[])
(meta #'f)
;; => {:arglists ([]),
;; :doc "Boring doc",
;; :line 32,
;; :column 1,
;; :file "C:/Users/teodorlu/IdeaProjects/th-scratch/src/th/play/core.clj",
;; :name f,
;; :ns #object[clojure.lang.Namespace 0x3b402f0c "th.play.core"]}
Now, redefine it!
(alter-meta! #'f assoc :rr 5)
(meta #'f)
;; => {:arglists ([]),
;; :doc "Boring doc",
;; :line 32,
;; :column 1,
;; :file "C:/Users/teodorlu/IdeaProjects/th-scratch/src/th/play/core.clj",
;; :name f,
;; :ns #object[clojure.lang.Namespace 0x3b402f0c "th.play.core"],
;; :rr 5}
Where assoc sets a value in a map.
(assoc {} :rr 5)
;; {:rr 5}
(assoc {:some :stuff} :more :stuff)
;; {:some :stuff, :more :stuff}
References
If you're confused by the #'f, this is how you get the var representing the binding of f, instead of just the value it refers to. For more information about vars and how to use them, refer to the official reference on vars and the less terse guide from 8th light.

clojure same ":or" value for all keys

I've defined a record with a bunch of fields--some of which are computed, some of which don't map directly to keys in the JSON data I'm ingesting. I'm writing a factory function for it, but I want to have sensible default/not-found values. Is there a better way that tacking on :or [field1 "" field2 "" field3 "" field4 ""...]? I could write a macro but I'd rather not if I don't have to.
There are three common idioms for implementing defaults in constructor functions.
:or destructoring
Example:
(defn make-creature [{:keys [type name], :or {type :human
name (str "unnamed-" (name type))}}]
;; ...
)
This is useful when you want to specify the defaults inline. As a bonus, it allows let style bindings in the :or map where the kvs are ordered according to the :keys vector.
Merging
Example:
(def default-creature-spec {:type :human})
(defn make-creature [spec]
(let [spec (merge default-creature-spec
spec)]
;; ....
))
This is useful when you want to define the defaults externally, generate them at runtime and/or reuse them elsewhere.
Simple or
Example:
(defn make-creature [{:keys [type name]}]
(let [type (or type :human)
name (or name (str "unnamed-" (name type)))]
;; ...
))
This is as useful as :or destructoring but only those defaults are evaluated that are actually needed, i. e. it should be used in cases where computing the default adds unwanted overhead. (I don't know why :or evaluates all defaults (as of Clojure 1.7), so this is a workaround).
If you really want the same default value for all the fields, and they really have to be different than nil, and you don't want to write them down again, then you can get the record fields by calling keys on an empty instance, and then construct a map with the default values merged with the actual values:
(defrecord MyFancyRecord [a b c d])
(def my-fancy-record-fields (keys (map->MyFancyRecord {})))
;=> (:a :b :c :d)
(def default-fancy-fields (zipmap my-fancy-record-fields (repeat "")))
(defn make-fancy-record [fields]
(map->MyFancyRecord (merge default-fancy-fields
fields)))
(make-fancy-record {})
;=> {:a "", :b "", :c "", :d ""}
(make-fancy-record {:a 1})
;=> {:a 1, :b "", :c "", :d ""}
To get the list of record fields you could also use the static method getBasis on your record class:
(def my-fancy-record-fields (map keyword (MyFancyRecord/getBasis)))
(getBasis is not part of the public records api so there are no guarantees it won't be removed in future clojure versions. Right now it's available in both clojure and clojurescript, it's usage is explained in "Clojure programming by Chas Emerick, Brian Carper, Christophe Grand" and it's also mentioned in this thread during a discussion about how to get the keys from a record. So, it's up to you to decide if it's a good idea to use it)

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?
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.
(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}}}

programmatically create new namespace and put vars in there from another namespace

Say I am in test.core and I want to create test.new-ns and stick a var called new-method in there. Is there a way to do that without leaving test.core?
This is what I want to do:
(ns test.core)
(create-ns 'test.new-ns)
(put-in-ns 'test.new-ns 'new-method {:meta 1} {:value 1})
*ns* ;=> test.core
test.new-ns/new-method ;;=> {:value 1}
(meta #'test.new-ns/new-method) ;; => {:meta 1}
create-ns actually exists in clojure.core and has exactly this signature.
As for put-in-ns, that's called intern; also, rather than accepting a metadata map as a separate argument, it transfers any metadata attached to the "name" symbol to the Var:
(intern 'test.new-ns (with-meta 'new-method {:meta 1}) {:value 1})
Reader meta works too, as long as you put it "inside the quote":
(intern 'test.new-ns ' ^{:meta 1} new-method {:value 1})
I believe you're looking for intern.
Finds or creates a var named by the symbol name in the namespace
ns (which can be a symbol or a namespace), setting its root binding
to val if supplied. The namespace must exist. The var will adopt any
metadata from the name symbol. Returns the var.
So for your example, it would go a little something like this:
(ns test.core)
(create-ns 'test.new-ns)
(intern 'test.new-ns 'new-method {:value 1})

In Clojure, how to get the name string from a variable or function?

I wanna get the string representation of a variable. For example,
(def my-var {})
How to get the string "my-var" from symbol my-var? And
(defn my-fun [] ...)
How to get the string "my-fun" from function my-fun?
user=> (def my-var {})
#'user/my-var
user=> (defn my-fun [] )
#'user/my-fun
user=> (name 'my-var)
"my-var"
user=> (name 'my-fun)
"my-fun"
user=> (doc name)
-------------------------
clojure.core/name
([x])
Returns the name String of a string, symbol or keyword.
nil
Every Var in Clojure has :name metadata attached.
user> (def my-var {})
#'user/my-var
user> (:name (meta #'my-var))
my-var
user> (let [a-var #'my-var]
(:name (meta a-var)))
my-var
However, usually if you already have the Var, then you already know the name anyway, and usually you don't use Vars in a program (i.e., you just pass my-var or my-fun rather than #'my-var and #'my-fun).
There's nothing to get the Var (or var-name) of a function or a value that happens to be the value of some Var. A Var knows its value, but not the other way round. That of course makes sense since, e.g., the very same function may be the value of zero (for local functions) or multiple vars.
How about this?
(defn symbol-as-string [sym] (str (second `(name ~sym)))
=> (def my-var {})
#'user/my-var
=> (symbol-as-string my-var)
"my-var"
=> (symbol-as-string 'howdy)
"howdy"
Doesn't work for function or macro names though, maybe someone can help me
=> (symbol-as-string map)
"clojure.core$map#152643"
=> (symbol-as-string defn)
java.lang.Exception: Can't take value of a macro: #'clojure.core/defn (NO_SOURCE_FILE:31)