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)
Related
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 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 ->
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)
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.
I am trying to create a clojure macro that converts an entered symbol to a string. However, when I do this:
(defmacro convert-to-string [something]
`(call-converted "~something")
)
(macroexpand '(convert-to-string convert-this))
: I get :
(call-converted "~something")
: instead of :
(call-converted "~convert-this")
: does anyone show me how I could do this?
You might consider using keywords (or quoted symbols) and a function, rather than a macro:
(defn convert-to-string [x] (call-converted (name x)))
(convert-to-string :foo)
(convert-to-string 'foo)
If you really want a macro:
(defmacro convert-to-string [x] `(call-converted ~(name x)))
(macroexpand-1 '(convert-to-string foo))
=> (user/call-converted "foo")
I am in no way an expert on macros, but would this solve your problem:
(defmacro to-str [expr] (str expr))