robert.hooke adds hooks around methods as follows:
(add-hook #'fully.qualified/name-to-override
(fn [f & args] 42)) ; override with lambda to return 42
I want to iterate over a vector of Symbols in order to add hooks to a number of functions, like so:
(doseq [f '[func-a func-b func-c]]
(add-hook (symbol "fully.qualified" (str f))
(fn [& args] 42)))
However, this doesn't work, because the symbol generated by (symbol s1 s2) isn't actually the function object itself. What's the correct way to do this? I've tried passing the (symbol ...) to resolve-ns too, but that doesn't help.
Use ns-resolve on the symbol.
(ns-resolve 'fully.qualified f)
Related
I have this ns with a macro in it. The annoying thing im dealing with is that the taoensso.timbre macro only works as a variadic expression (timbre/info "a" "b" "c"). A list of items wont log right (timbre/info ["a" "b" "c"]). Im trying to create a wrapper macro that lets the code call (logger/info) in the same variadic form, then process all elements, and then pass to timbre/info
(ns logger
(:require [taoensso.timbre :as timbre :include-macros true])) ; a third party logger
;; A bit of pseudo code here. If you pass in a vector of args, you should get a vector of args with some changes
(defn scrub [args]
(if (listy)
(mapv (fn [a] (scrub args) args)
(if (is-entry a) {:a "xxx"} a)
(defmacro info
[& args]
`(timbre/info ~#(scrub args)))
This doesnt work because scrub is called immediately and wont resolve symbols passed in. I need something like either of these that dont work.
(defmacro info
[& args]
`(timbre/info #(scrub-log-pii ~args)))
(defmacro info
[& args]
`(timbre/info ~#('scrub-log-pii args)))
My last thought was to try to wrap the timbre macro in a function so the macro and evaluation happen in the right order. There is however, no way to "apply" to a macro.
(defn info3
[& args]
(timbre/info (scrub-log-pii (vec args))))
Any ideas?
not exactly an answer to the question as phrased (macro application stuff), but rather the practical timbre solution, that may be applicable in your specific case:
here you can see that all timbre macros use log! macro, which in turn accepts the collection of args.
so, just implementing your procedure as
(defmacro info* [args] `(log! :info :p ~args ~{:?line (fline &form)}))
should do the trick.
You have encountered a problem of using macros known as "turtles all the way down". That is, instead of using function composition, you may need to write a wrapper macro, then another wrapper macro for that, etc.
The detailed steps to writing a macro are described in this answer:
How do I write a Clojure threading macro?
For your specific problem, we could to this:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.pprint :as pp]))
(defn infix-impl
[a op b]
(list op a b))
(defmacro infix
"Allows user to have Java-style infix operators:
(infix 2 + 3)
"
[a op b] (infix-impl a op b))
(defn infix-seq-impl
[args]
`(let [form# (cons 'infix ~args)
result# (eval form#)]
result#))
(defmacro infix-seq
[args] (infix-seq-impl args))
(dotest
(is= 5 (infix 2 + 3))
(let [params '[2 + 3]]
(pp/pprint (infix-seq-impl 'params))
(is= 5 (infix-seq params))))
Here we use the infix macro to show how to create a wrapper macro infix-seq that accepts a sequence of params instead of N scalar params. The printed output shows the generated code:
(clojure.core/let
[form__24889__auto__ (clojure.core/cons 'tst.demo.core/infix params)
result__24890__auto__ (clojure.core/eval form__24889__auto__)]
result__24890__auto__)
A more general version
The applied macro below allows you to pass in the name of the macro to be "applied" to the param sequence:
(defn applied-impl
[f args]
`(let [form# (cons ~f ~args)
result# (eval form#)]
result#))
(defmacro applied
[f args] (applied-impl f args))
(dotest
(nl)
(let [params '[2 + 3]]
; (applied 'infix params) ; using a single quote fails
(is= 5 (applied `infix params)) ; using a backquote works
(is= 5 (applied 'tst.demo.core/infix params)) ; can also use fully-qualified symbol with single-quote
(is= 5 (applied (quote tst.demo.core/infix) params)) ; single-quote is a "reader macro" for (quote ...)
))
Here's a working minimal example showing how Clojure can handle non-namespaced symbols:
(defmacro simple-macro [s]
(name `~s))
(str "And the answer is "
(simple-macro v1))
Now I'd like to do something more complicated. Inspired by this example:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
(Q 1)
(Q 2)
;; [1 2]
I now want to define a similar closure to take the names of undefined variables.
(defn take-names-fun []
(let [names (atom [])]
#((swap! names conj (simple-macro %)) (deref names))))
(def P (take-names-fun))
(P v1)
But this doesn't work as hoped; I get the error:
Unable to resolve symbol: v1 in this context
Is there a way to fix this so that we can add the name "v1" to the list of names defined above?
I tried using a macro instead (inspired by a syntax trick on page 21 of "Mastering Clojure Macros")... but this answer on ask.clojure.org says it doesn't make sense to define a closure over an atom in a macro.
(defmacro take-names-macro []
(let [names (atom [])]
`(fn [~'x] (swap! ~names conj (simple-macro ~'x)) (deref ~names))))
(def R (take-names-macro))
And indeed, I get another error here:
Can't embed object in code, maybe print-dup not defined:
However, there is no such restriction for using atoms inside defn. Maybe at the end of the day I need to put my symbols in a namespace...?
Not quite sure what it is that you're ultimately trying to accomplish.
But, since P is a function, it will always evaluate its arguments. So, if you pass it an undefined symbol, you'll get the error you got. Instead, you have to create a macro so that you can quote the undefined symbol (to stop the evaluation of the argument) and then pass that to P. Here is an example that does that.
user> (defn take-names-fun []
(let [names (atom [])]
(fn [arg] (swap! names conj (name arg)))))
#'user/take-names-fun
user> (def P (take-names-fun))
#'user/P
user> (defmacro PM [s] `(P (quote ~s)))
#'user/PM
user> (PM v1)
["v1"]
user> (PM v2)
["v1" "v2"]
user>
You might find the article on Evaluation in Clojure helpful.
#dorab's answer is nice.
But you could also tell yourself: "When entering undefined variables into a function, I have to quote them to avoid evaluation of them!"
So, after:
(defn typical-closure []
(let [names (atom [])]
(fn [arg] (swap! names conj arg) #names)))
(def Q (typical-closure))
Do:
user=> (Q 'v1)
[v1]
user=> (Q 'v2)
[v1 v2]
user=> (Q 3)
[v1 v2 3]
user=> (Q 'v4)
[v1 v2 3 v4]
user=>
In this way you don't need the macro and you can alternate between evaluated and not-evaluated arguments (undefined symbols).
So with the way fn's are written in clojure there is unfortunately no way to get the name of the var being passed as a param from within the fn body.. Someone with more experience with the clojure src may be able to explain better why that is, my initial guess would be that it has something to do with keeping thread local scopes isolated and lazy.
But there's absolutely nothing stopping you from writing a macro that wraps other macros using your closure idea!
Here's an example of how something like that may be written:
https://stackoverflow.com/a/11857444
In clojure, can one idiomatically obtain a function's name inside of its body, hopefully accomplishing so without introducing a new wrapper for the function's definition? can one also access the function's name inside of the body of the function's :test attribute as well?
For motivation, this can be helpful for certain logging situations, as well as for keeping the body of :test oblivious to changes to the name of the function which it is supplied for.
A short elucidation of the closest that meta gets follows; there's no this notion to supply to meta, as far as I know, in clojure.
(defn a [] (:name (meta (var a))))
Obviously it is easy to accomplish with a wrapper macro.
Edit: luckily no one so far mentioned lambda combinators.
There are 2 ways to approach your question. However, I suspect that to fully automate what you want to do, you would need to define your own custom defn replacement/wrapper.
The first thing to realize is that all functions are anonymous. When we type:
(defn hello [] (println "hi"))
we are really typing:
(def hello (fn [] (println "hi"))
we are creating a symbol hello that points to an anonymous var which in turn points to an anonymous function. However, we can give the function an "internal name" like so:
(def hello (fn fn-hello [] (println "hi")))
So now we can access the function from the outside via hello or from the inside using either hello of fn-hello symbols (please don't ever use hello in both locations or you create a lot of confusion...even though it is legal).
I frequently use the fn-hello method in (otherwise) anonymous functions since any exceptions thrown will include the fn-hello symbol which makes tracking down the source of the problem much easier (the line number of the error is often missing from the stack trace). For example when using Instaparse we need a map of anonymous transform functions like:
{
:identifier fn-identifier
:string fn-string
:integer (fn fn-integer [arg] [:integer (java.lang.Integer. arg)])
:boolean (fn fn-boolean [arg] [:boolean (java.lang.Boolean. arg)])
:namespace (fn fn-namespace [arg] [:namespace arg])
:prefix (fn fn-prefix [arg] [:prefix arg])
:organization (fn fn-organization [arg] [:organization arg])
:contact (fn fn-contact [arg] [:contact arg])
:description (fn fn-description [arg] [:description arg])
:presence (fn fn-presence [arg] [:presence arg])
:revision (fn fn-revision [& args] (prepend :revision args))
:iso-date (fn fn-iso-date [& args] [:iso-date (str/join args)])
:reference (fn fn-reference [arg] [:reference arg])
:identity (fn fn-identity [& args] (prepend :identity args))
:typedef (fn fn-typedef [& args] (prepend :typedef args))
:container (fn fn-container [& args] (prepend :container args))
:rpc (fn fn-rpc [& args] (prepend :rpc args))
:input (fn fn-input [& args] (prepend :input args))
...<snip>...
}
and giving each function the "internal name" makes debugging much, much easier. Perhaps this would be unnecessary if Clojure had better error messages, but that is a longstanding (& so far unfullfilled) wish.
You can find more details here: https://clojure.org/reference/special_forms#fn
If you read closely, it claims that (defn foo [x] ...) expands into
(def foo (fn foo [x] ...))
although you may need to experiment to see if this has already solved the use-case you are seeking. It works either way as seen in this example where we explicitly avoid the inner fn-fact name:
(def fact (fn [x] ; fn-fact omitted here
(if (zero? x)
1
(* x (fact (dec x))))))
(fact 4) => 24
This version also works:
(def fact (fn fn-fact [x]
(if (zero? x)
1
(* x (fn-fact (dec x))))))
(fact 4) => 24
(fn-fact 4) => Unable to resolve symbol: fn-fact
So we see that the "internal name" fn-fact is hidden inside the function and is invisible from the outside.
A 2nd approach, if using a macro, is to use the &form global data to access the line number from the source code. In the Tupelo library this technique is used to improve error messages for the
(defmacro dotest [& body] ; #todo README & tests
(let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
`(clojure.test/deftest ~test-name-sym ~#body)))
This convenience macro allows the use of unit tests like:
(dotest
(is (= 3 (inc 2))))
which evalutes to
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
instead of manually typing
(deftest t-addition
(is (= 3 (inc 2))))
You can access (:line (meta &form)) and other information in any macro which can make your error messages and/or Exceptions much more informative to the poor reader trying to debug a problem.
Besides the above macro wrapper example, another (more involved) example of the same technique can be seen in the Plumatic Schema library, where they wrap clojure.core/defn with an extended version.
You may also wish to view this question for clarification on how Clojure uses the "anonymous" var as an intermediary between a symbol and a function: When to use a Var instead of a function?
I started with the following code (imagine more than this, but I think this gets the point across):
(defn fun1 [arg] {:fun1 arg})
(defn funA [arg] {:funA arg})
(defn funOne [arg] {:funOne arg})
(defn funBee [arg] {:funBee arg})
(defn -main [& args] (prn (fun1 "test-data")))
My next pass rendered it so:
(defmacro item-defn [a]
`(defn ~(symbol a) [arg#] {~(keyword a) arg#}))
(item-defn "fun1")
(item-defn "funA")
(item-defn "funOne")
(item-defn "funBee")
(defn -main [& args] (prn (fun1 "test-data")))
Is there a way to get this down to something like:
(defmacro item-defn [a]
`(defn ~(symbol a) [arg#] {~(keyword a) arg#}))
(map #(item-defn %) ["fun1" "funA" "funOne" "funBee"])
(defn -main [& args] (prn (fun1 "test-data")))
(I tried that in the repl, and it seems to work, but when I load a clj file with it in it, then it doesn't work. It gives me a "CompilerException" "Unable to resolve symbol: fun1")
Am I misusing macros? How would you do this?
You may define another macro for this purpose, e.g.:
(defmacro item-defn [a]
`(defn ~(symbol a) [arg#] {~(keyword a) arg#}))
(defmacro items-defn [& names]
`(do ~#(for [n names] `(item-defn ~n))))
then you'll be able to use it to define any number of functions:
(items-defn "fun1" "funA" "funOne" "funBee")
I wonder if you map expression really works in the REPL. I suspect that the fun1 and funA functions you have are still in your REPL because you first eval-ed (item-defn "fun1") and (item-defn "funA"). On my box I get:
(map #(item-defn %) ["fun1" "funA"])
;=> (#'user/p1__22185# #'user/p1__22185#)
So no function is defined with name fun1 or funA. The problem is that map is a function and item-defn is a macro. What happens in your map epxression is that item-defn gets macroexpanded at compile time at which moment the strings with function names are not visible. The macroexpander has no way of knowing that you want to use "fun1" as a name for your to be defn-ed function. Instead the macroexpander just sees % and then uses a gen-symed name as name of the defn-ed function. The map expression is evaluated at runtime but then it is too late for the macroexpanded function to do anything with the supplied strings.
The solution of Leonid works because he uses another macro to iterate over the function names. So that the iteration also happens at compile time. You see, macros are kind of contagious. Once you start, you cannot stop.
Inside your macro, the name is already a symbol so you can do:
(defmacro item-defn [name]
`(defn ~name [arg#] {~(keyword name) arg#}))
then
(item-defn fun1)
Here is the sample code I want to get to work:
(letfn [(CONC [f] f)
(CONT [f] (str "\newline" f))]
((voodoo "CONC") "hamster"))
Is there some voodo that will make it call the CONC function with hamster as the parameter? That is, is there some way to convert the string "CONC" into a function that is not bound to a namespace but rather to a local binding?
EDIT:
To be clearer, the way this will be called is:
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
I'd probably solve this by creating a map of functions indexed by strings:
(def voodoo
{"CONC" (fn [f] f)
"CONT" (fn [f] (str "\newline" f))})
Then your desired code should work directly (exploiting the fact that a map is a function that looks up it's argument)
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
Note that the functions here are fully anonymous - you don't need them to be referenced anywhere in the namespace for this to work. In my view this is a good thing, because unless you also need the functions somewhere else then it's best to avoid polluting your top-level namespace too much.
No. Eval does not have access to the local/lexical environment, ever.
Edit: This is not a very good answer, and not really accurate either. You could write voodoo as a macro, and then it doesn't need runtime access to the lexical environment, just compile-time. However, this means it would only work if you know at compile time that the function you want to call is x, and so it wouldn't be very useful - why not just type x instead of (voodoo "x")?
(defmacro voodoo [fname]
(symbol fname))
(letfn [(x [y] (inc y))]
((voodoo "x") 2))
;; 3
(letfn [(x [y] (inc y))]
(let [f "x"]
((voodoo f) 2)))
;; error
Well, it's sort of possible:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(throw (RuntimeException. "no such local")))))
...and now we can do weird stuff like this:
user> (defn example [s]
(letfn [(foo [x] {:foo x})
(bar [x] {:bar x})]
((voodoo s) :quux)))
#'user/example
user> (example "foo")
{:foo :quux}
user> (example "bar")
{:bar :quux}
user> (example "quux")
; Evaluation aborted.
user> *e
#<RuntimeException java.lang.RuntimeException: no such local>
That "Evaluation aborted" means an exception was thrown.
You could also replace the throw branch of the if in voodoo with (resolve (symbol ~s)) to defer to the globals if no local is found:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(resolve (symbol ~s)))))
...and now this works with definition of example as above (though note that if you are experimenting at the REPL, you will need to recompile example after redefining voodoo):
user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}
Now, this is an abuse of Clojure's facilities which one would do well to try to do without. If one cannot, one should probably turn to evalive by Michael Fogus; it's a library which provides an "eval-with-locals" facility in the form of an evil function and a couple of utilities. The functionality seems to be well factored too, e.g. something like the ~(zipmap ...) thing above is encapsulated as a macro and evil there appears to be almost a drop-in replacement for eval (add the env parameter and you're good to go). I haven't read the source properly, but I probably will now, looks like fun. :-)
Im not really clear what you are asking for so i'll try a couple answers:
if you have a string that is the name of the function you wish to call:
(def name "+")
((find-var (symbol (str *ns* "/" name))) 1 2 3)
this would give voodoo a deffinition like this:
(defn voodoo [name args] (apply (find-var (symbol (str *ns* "/" name))) args))
#'clojure.core/voodoo
clojure.core=> (voodoo "+" [1 2 3])
6
clojure.core=>
this assumes your function is in the current namepace ns.
if you want to turn a string into a function you could use this pattern
(let [f (eval (read-string "(fn [] 4)"))] (f))