I couldn't understand the difference between macroexpand and macroexpand-1.
Could you provide examples?
Let's say we have the following code:
(defmacro inner-macro [arg]
`(println ~arg))
(defmacro top-level-macro [arg]
`(inner-macro ~arg))
(defn not-a-macro [] nil)
Then, doc of macroexpand-1 says:
If form represents a macro form, returns its expansion,
else returns form.
Indeed, it does:
user> (macroexpand-1 '(inner-macro "hello"))
(clojure.core/println "hello")
user> (macroexpand-1 '(top-level-macro "hello"))
(user/inner-macro "hello")
user> (macroexpand-1 '(not-a-macro))
(not-a-macro)
In other words, macroexpand-1 does just one step of macroexpansion if supplied form is a macro form.
Then, doc of macroexpand:
Repeatedly calls macroexpand-1 on form until it no longer
represents a macro form, then returns it.
Example:
user> (macroexpand '(top-level-macro "hello"))
(clojure.core/println "hello")
What happened? As soon as, (top-level-macro "hello") expands to (user/inner-macro "hello"), which is macro form, macroexpand will perform expansion once again. The result of second expansion is (clojure.core/println "hello"). It is not a macro form, so macroexpand just returns it.
So, just to rephrase the doc, macroexpand will recursively do expansion until top level form is not a macro form.
Also there's additional note in macroexpand's doc:
Note neither macroexpand-1 nor macroexpand expand macros in subforms.
What does that mean? Let's say we have one more macro:
(defmacro subform-macro [arg]
`(do
(inner-macro ~arg)))
Let's try to expand it:
user> (macroexpand-1 '(subform-macro "hello"))
(do (user/inner-macro "hello"))
user> (macroexpand '(subform-macro "hello"))
(do (user/inner-macro "hello"))
Since, (do ...) form is not a macro macroexpand-1 and macroexpand just return it and nothing more. Don't expect that macroexpand will do the following:
user> (macroexpand '(subform-macro "hello"))
(do (clojure.core/println "hello"))
the difference is quite simple. First of all the background: when the compiler sees macro call it tries to expand it according to its definition. If the code, generated by this macro contains other macros, they are also expanded by compiler, and so on, until resulting code is totally macros-free. So macroexpand-1 just expands the topmost macro and shows the result (no matter does it generate another macro calls), while macroexpand tries to follow the compiler's pipeline (partially, not expanding macros in subforms. To make the complete expansion you should take a look at clojure.walk/maxroexpand-all).
small example:
user> (defmacro dummy [& body]
`(-> ~#body))
#'user/dummy
this silly macro generates the call to another macro ( -> )
user> (macroexpand-1 '(dummy 1 (+ 1)))
(clojure.core/-> 1 (+ 1))
macroexpand-1 just expands dummy, but keeps -> unexpanded
user> (macroexpand '(dummy 1 (+ 1)))
(+ 1 1)
macroexpand expands dummy and then expands ->
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 ...)
))
user=> (def v-1 "this is v1")
user=> (def v-2 "this is v2")
user=> (defmacro m [v] (symbol (str "v-" v)))
user=> (m 1)
"this is v1"
user=> (m 2)
"this is v2"
user=> (let [i 2] (m i))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: v-i in this context, compiling:(NO_SOURCE_PATH:73:12)
Can I write a macro let both
(m 2)
and
(let [i 2] (m i))
get "this is v2" ?
This is possible without a macro:
(defn m [v] (var-get (resolve (symbol (str "v-" v)))))
(m 1) ;; => "This is v1"
(let [i 2] (m i)) ;; => "This is v2"
You can use a macro too if you want:
(defmacro m [v] `#(resolve (symbol (str "v-" ~v))))
A plain function seems much more likely to be what you want.
First, though, to address the original question, if you wanted to insist on using a macro, macros are regular functions that happen to be called at compile time, so you can look up a Var using its symbolic name and obtain its value using deref just like you could at (your application's, as opposed to your macro's) runtime:
(defmacro var-value [vsym] #(resolve vsym))
(def foo 1)
(var-value foo)
;= 1
(macroexpand-1 '(var-value foo))
;= 1
Note that the above 1 is the actual macroexpansion here. This is different to
(defmacro var-value [vsym] `#(resolve ~vsym))
in that the latter expands to a call to resolve, and so the lookup given that implementation is postponed to your app's runtime.
(macroexpand-1 '(var-value foo))
;= (clojure.core/deref (clojure.core/resolve foo))
So this code will just be inlined wherever you call the macro.
Of course the macro could also expand to a symbol – e.g.
(defmacro prefixed-var [suffix]
`(symbol (str "v-" ssuffix)))
will produce expansions like v-1 (for (prefixed-var 1)) etc.
Going back to the subject of the suitability of macros here, however, if you use a macro, all the information that you need to produce your expansion must be available at compile time, and so in general you cannot use the values of let / loop locals or function arguments in your expansion for the fundamental reason that they don't have any fixed value at compile time.1
Thus the cleanest approach would probably be to wrap a resolve call in defn and call the resulting function – although of course to know for sure, we'd need to know what problem you were trying to solve by introducing a macro that performs a Var lookup.
1 Except if statically assigned constant values, as in the example given in the question text; I'm assuming you're thinking of using runtime values of locals in general, not just those that whose initialization expressions are constant literals.
I have the following macro:
(defmacro my-macro [k]
`(do
(def pair
[
k
~(symbol (str "-" (name k)))]
)))
...which expands to:
(macroexpand-1 `(my-macro :n/k))
(do (def user/pair [user/k -k]))
...but instead I would like it to expand to
(do (def user/pair [:n/k -k]))
How can I make the macro keep the keyword and its namespace?
Thanks!
You need to escape k from the syntax quote using ~k:
(defmacro my-macro [k]
`(def ~'pair [~k ~(symbol (str "-" (name k)))]))
I've made a few other changes here as well:
Idiomatic formatting. Don't put ( or [ at the end of a line -- and put closing ) and ] on the same line as the expression they close.
do is entirely superfluous here.
If you want the macro to expand to (def pair ...), then you need to
escape out of the syntax quote (~)
quote the symbol pair (i.e., 'pair)
Putting this together, you have ~'pair. The reason you have to do this is because, in Clojure, `<symbol> is read as (quote <current-namespace>/foo>), where <current-namespace> stands for the current namespace. But def doesn't take names that are namespaced. Hence the ~' dance.
(But you probably want to parameterize on pair anyway ... otherwise, it's not very useful to use my-macro more than once per namespace.)
Overall, this seems like a very odd macro. I don't know what you're trying to accomplish, but I would probably take a different approach.
you can use the namespace and name function to extract the parts you want from the keyword passed in and combine them as required:
user> (defmacro my-macro [k]
`(do
(def pair
[~(keyword (str (namespace k) "/" (name k)))
~(symbol (str "-" (name k)))])))
#'user/my-macro
user> (macroexpand-1 `(my-macro :n/k))
(do (def user/pair [:n/k -k]))
Revised Answer
There are 2 things a bit confusing about your question & I misread it earlier.
You should use a regular single-quote ' with macroexpand-1, not the back-tic `. The back-tick is normally used only in a macro definition to delineate a piece of "template code".
I just noticed that the arg in the macro definition is k, and the keyword you use in the example is :n/k. These duplicate names will cause confusion.
Let's restate the problem:
(ns clj.demo)
(defmacro my-macro [arg]
`(do
(def pair
[
arg
~(symbol (str "-" (name arg)))]
)))
(println (macroexpand-1 `(my-macro :n/k)))
;=> (do (def clj.demo/pair [clj.demo/arg -k]))
So we are in the clj.demo namespace, which gets applied to the symbols pair and arg. We need to substitue the argument arg using ~:
(ns clj.demo)
(defmacro my-macro [arg]
`(do
(def pair
[
~arg
~(symbol (str "-" (name arg)))]
)))
(println (macroexpand-1 '(my-macro :n/k)))
;=> (do (def clj.demo/pair [:n/k -k]))
Which is what you want.
I have the following macro :
(defmacro add-children [this children]
(map (fn [child] (list '.addChild this child)) children))
and I would like to create the following macro :
(defmacro defgom [name & body]
(let [sym (gensym)]
`(let [~sym (Model.)]
(add-children sym body)))))
Considering that Model is a Java class with an addChild function. I would like to expand defgom to
(let [*gensym* (Model.)]
(.addChild *gensym* (first body))
(.addChild *gensym* (second body))
...
(.addChild *gensym* (last body)))
When evaluated, the add-children macro gives the correct result (the list of .addChild). But I can't evaluate it in the defgom macro. I get a "Don't know how to create ISeq from: clojure.lang.Symbol". I tried with ~ or ~# (given that add-children returns a list), but none worked.
How to properly expand the macro inside the macro?
PS: I know I can do it with a function rather than the add-children macro, but I want to know if it's possible to do it with a macro.
Just change the last line to:
(add-children ~sym ~#body)
I'm attempting to create a macro similar to the Quartzite defjob macro that creates the Job class with the #DisallowConcurrentExecution annotation added to it. The code works from the repl, but not inside the macro.
This works...
user=> (defrecord ^{DisallowConcurrentExecution true} YYY []
#_=> org.quartz.Job
#_=> (execute [this context]
#_=> (println "whoosh!")))
user.YYY
user=> (seq (.getAnnotations YYY))
(#<$Proxy3 #org.quartz.DisallowConcurrentExecution()>)
...but this does not.
(defmacro defncjob
[jtype args & body]
`(defrecord ^{DisallowConcurrentExecution true} ~jtype []
org.quartz.Job
(execute [this ~#args]
~#body)))
After Rodrigo's suggestion, here is a way to make it work.
(defmacro defdcejob
[jtype args & body]
`(defrecord ~(vary-meta jtype assoc `DisallowConcurrentExecution true) []
org.quartz.Job
(execute [this ~#args]
~#body)))
You can't use the ^ reader macro inside macros. Take a look at this similar question.