Get the name of the enclosing method from stacktrace (clojure)? - clojure

I have the following in a file "constraint.clj" starting at line 49 (shown with line numbers):
49 (defn stacker []
50 (let [s (first (.getStackTrace (new java.lang.Throwable)))]
51 {:name (.getMethodName s)
52 :file (.getFileName s)
53 :line (.getLineNumber s)}))
54
55 (def s (stacker))
From nrepl, I compile the file. When I inspect the value at s, it shows.
app.constraint> s
{:name "invoke", :file "constraint.clj", :line 50}
So, basically, it seems to work pretty well, except that the getMethodName is not what I expected. I would like :name to be app.constraint/stacker. How do I do that?

Internally, Clojure generates a Java class inheriting from clojure.lang.AFn for each function matching the pattern "namespace$function-name". When a function is executed, the invoke method is called on that object, with the correct arity.
You can find the source here: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFn.java
If you want the original method, from the Java stack trace, you can look at the generated class name.
(defn stacker []
(let [s (first (.getStackTrace (new java.lang.Throwable)))]
{:name (clojure.main/demunge (.getClassName s))
:file (.getFileName s)
:line (.getLineNumber s)}))
(stacker) ;=> {:name "app.constraint/stacker", :file "constraint.clj", :line 50}
This information is also available directly through the function's metadata.
(meta #'stacker)

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 read object from a file and extract data

I am very new to closure so I am not fully sure how to do this. If I have a file data.txt with the following:
[
{:name "Steve"}
{:name "Issac"}
{:name "Lucas"}
{...}
]
I want to be able to read the contents of each :name tag and do something with the return value (in this case it will be printing to the console). I looked up online and found there is a method called reader and I understand how to open a file.
The Closure syntax confuses me slightly so I am not sure how to do this.
there should be 2 possiblities:
1) raw clojure by means clojure.core/read-string
(read-string "['q 2 \"test\"]")
;; [(quote q) 2 "test"]
2) via clojure.edn/read-string
(clojure.edn/read-string "['q 2 \"test\"]")
;; ['q 2 "test"]
the 2nd one should be faster and safer (but does not eval and stuff),
but is only for edn format (this is a subset of clojure code)
the string dummy (i.e from your data.txt)
;; a string, just for demo
(def s "[{:name \"Steve\" email: \"foo#bar.com\" }
{:name \"Issac\"}
{:name \"Lucas\"}]")
the rest is plain clojure, if you have trouble with clojure maps here is the doc
(doseq [name (map :name (clojure.edn/read-string s))]
(println name))
;; Steve
;; Issac
;; Lucas
;; nil

how does clojure add line numbers in the meta of an evaluated defn?

I've been playing around with the meta function and just for fun, I placed a meta around a function definition and evaluated it in emacs... I got this output:
(meta (defn has-signal [sigtype]
(fn [ex]
(-> ex ex-data :ribol.core/signal (= sigtype)))))
=> {:ns #<Namespace ribol.test-ribol>, :name has-signal,
:arglists ([sigtype]), :column 8, :line 1, :file "NO_SOURCE_PATH"}
How is this happening? how does clojure know what line number and what namespace the function is at?
The compiler knows this at the point that the code is evaled. It's the same process by which stack traces work.
If you eval in the repl, then this information is missing (NO_SOURCE_PATH) which is a pain.

In Clojure how do I get the basis for a record with only an instance of the record?

I have a record in Clojure
(defrecord Animal [name age])
and I can easily create an instance of the record
(def my-cat (Animal. "spot" 2))
I can also create an instance with the map->Animal function
(def my-dog (map->Animal {:name "snowy" :age 6 :legs 3}))
which creates
#user.Animal{:name "snowy", :age 6, :legs 3}
Given only an instance of Animal, eg my-dog, how do I get the basis?
I'm aware that I can make a static call on Animal to get it, like so:
(Animal/getBasis)
which gives [name age], but how do I get this from my-dog?
The problem seems to be with clojure's handling of static method calling. Here is some code which will get your answer
user=> (defrecord Animal [name age])
user.Animal
user=> (def my-dog (map->Animal {:name "snowy" :age 6 :legs 3}))
#'user/my-dog
user=> (. (. (type my-dog) getMethod "getBasis" nil) invoke nil nil)
[name age]
There is a longer discussion of this in another SO thread

Clojure: How to get meta-data of functions?

I am trying to get meta-data of all built-in Clojure functions.
In previous question I've learned that this can be achieved using something like ^#'func_name (get the var object's meta data). But I didn't manage to do it programmatically, where func-name is not known in advance.
For example trying to get the metadata of the last function in clojure.core:
user=> (use 'clojure.contrib.ns-utils)
nil
user=> (def last-func (last (vars clojure.core)))
user=> last-func
zipmap
;The real metadata (zipmap is hardcoded)
user=> ^#'zipmap
{:ns #<Namespace clojure.core>, :name zipmap, :file "clojure/core.clj", :line 1661, :arglists ([keys vals]), :doc "Returns a map .."}
;Try to get programmatically, but get shit
user=> ^#'last-func
{:ns #<Namespace user>, :name last-func, :file "NO_SOURCE_PATH", :line 282}
How can it be done? I tried numerous variations already, but nothing does the trick.
You are a looking for meta and ns-resolve.
user=> (let [fun "map"] (meta (ns-resolve 'clojure.core (symbol fun))))
{:ns #<Namespace clojure.core>, :name map, :file "clojure/core.clj", :line 1705, :arglists ([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls]), :doc "Returns a lazy sequence consisting of the result of applying f to the\n set of first i tems of each coll, followed by applying f to the set\n of second items in each coll, until any one of the colls is\n exhausted. Any remaining items in other colls are ignored. Function\n f should accept number-of-colls arguments."}
Technically functions cannot have metadata in Clojure currently:
http://www.assembla.com/spaces/clojure/tickets/94-GC--Issue-90---%09-Support-metadata-on-fns
However, vars which are bound to functions may, and it looks like that's what you're finding with ns-resolve. (meta last-func) would work too. Since last-func is the var itself, ^#'last-func (which is shorthand for (meta (var (quote last-func)))) has a redundant var dereferencing.