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.
Related
Is there a way to retrieve the metadata of the arguments inside a clojure macro without using eval? The only thing I could come up with so far is this:
(def ^{:a :b} my-var)
(defmacro my-macro [s] (prn (eval `(meta (var ~s)))))
(my-macro my-var)
;; Prints {:a :b, :name my-var, ...}
I ended up finding a solution:
(def ^{:a :b} my-var)
(defmacro my-macro [s] (prn (meta (resolve s))))
(my-macro my-var)
;; Prints {:a :b, :name my-var, ...}
So the key part here is to use resolve function to get the var associated to the symbol.
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}}}
I have this code and would like to get the metadata transform
(defn truncate
[& {:keys [len]}]
(fn ^:transform [value]
(clojure.string/join (take len value))))
Ex: (meta (var (truncate)) //doesn't work
Something like this is possible? (meta (meta (var truncate))
UPDATE:
I moved it top the function name and solved it this way:
(defn- func-meta [func]
(let [[name-space func-name _] (clojure.string/split (str func) #"\$")]
(meta (ns-resolve (symbol name-space) (symbol func-name)))))
(func-meta (transform/truncate)) ;=> metadata
Attaching this type of metadata to arglists has no particular meaning in Clojure. (Type hints may be attached to arglists, but that's a different matter.) You can, however, attach metadata to the function itself using either of the following methods:
(defn foo []
^:foo (fn [] 1))
(defn foo []
(with-meta (fn [] 1) {:foo 1}))
;; in either case:
(meta (foo))
;= {:foo true}
Also, the var special form gives convenient access to Vars:
(var +)
;= #'clojure.core/+
The #' shorthand notation is used much more frequently.
I'm trying to call clojure.walk/stringify-keys on a map that might include record instances. Since stringify-keys is recursive, it attempts to convert the keys on my record, (since (map? record-var) is true) which causes an error. Is there any way to tell if a var is a record rather than just a Clojure map? I
d like to provide my own implementation of stringify-keys that is record-aware.
The current implementation of stringify-keys causes the following:
(use '[clojure.walk :only [stringify-keys]])
(defrecord Rec [x])
(let [record (Rec. "foo")
params {:x "x" :rec record}]
(stringify-keys params))
This causes the following exception: UnsupportedOperationException Can't create empty: user.Rec user.Rec (NO_SOURCE_FILE:1)
Records seem to implement the IRecord marker interface:
user=> (defrecord Rec [x])
user.Rec
user=> (require '[clojure.reflect :as r])
nil
user=> (:bases (r/reflect Rec))
#{java.io.Serializable clojure.lang.IKeywordLookup clojure.lang.IPersistentMap clojure.lang.IRecord java.lang.Object clojure.lang.IObj clojure.lang.ILookup java.util.Map}
user=> (instance? clojure.lang.IRecord (Rec. "hi"))
true
Update
1.6 now has the record? functions
you can check the type of each member and see if it is really a map or something else (where something else is presumed to be a record)
user> (type {:a 1 :b 2})
clojure.lang.PersistentArrayMap
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.