I would like to redefine some functions in my program on startup according to some metadata on those functions.
I am new to clojure so I would like what is the idiomatic way to accomplish this.
What I would like to do is use a cache (like memcache) to cache results of some functions (database results). In similar way like memoize or contrib core.cache, but i would like to redefine original functions transparently to the rest of the program, according to metadata which defines the caching policy.
Java libraries usually use annotations and code generation to accomplish this. But I am wandering what is idiomatic clojure way of accomplishing this?
I have explored a few options on the internet but they don't seem too satisfactory. Binding is not what I want because it only works on the current thread. Other options seems to be using some internal java functions which I would like to avoid, or binding ns and rerefining functions with eval.
I understand that i can list the potential functions in one namespace with (keys (ns-publics 'foo)), but have not explored yet how to list non public functions and how to list available namespaces (currently loaded?) - maybe there is namespace loading hook i can use?
EDIT: This is a small example of what I have in mind. Wrap is a function that would perform caching according to origs metadata. Caching and metadata are absent from example, and both wrap and orig are in the same namespace.
(defn orig []
"OK")
(defn orig2 []
"RES 2")
(defn wrap [f & args]
(let [res (apply f args)]
println "wrap" f args "=" res
res))
(set! orig (wrap orig))
(set! orig2 (wrap orig2))
After evaluating last two forms orig and orig2 should be redefined to use wrapped versions. Unfortunately I get the following error in REPL:
java.lang.IllegalStateException: Can't change/establish root binding of: orig with set (NO_SOURCE_FILE:0)
You can use def/defn
again and it will change the definition of the function (technically it will write a new definition which uses the same name).
So you could just do:
(def orig (wrap orig))
(def orig2 (wrap orig2))
Also, if I understand your intention: wrap
should be returning a function, not a result.
(defn wrap [f]
(fn [& args]
(let [res (apply f args)]
(println "wrap" f args "=" res)
res)))
If you look at the memoize
standard function it works in precisely this way.
Clojure stores functions in vars
to make this sort of code manipulation easier. you can just change the value of the var to reference the proper function.
if you have a list of potential caching functions then you can define them in their own namespace and have a bit of code that uses def
to set the top level binding to point at the appropriate one.
(defn cache-all ...)
(defn cache-some ...)
(defn cache-none ...)
(let [policy (get-current-policy)]
(cond (= policy :all) (def cacher cache-all)
(= policy :some) (def cacher cache-some)
...
))
if you need to actually define the function based on new input then eval
is the idomatic approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With