I want to create scheme like define macro, here is my try:
(defmacro define [list & body]
`(defn ,(first list) [~#(rest list)] ~body))
but when I run:
(define (foo a b) (+ a b))
I've got error: java.lang.Exception: First argument to def must be a Symbol (NO_SOURCE_FILE:18)
what's wrong with my macro?
You need to use ~ to unquote the symbol name:
(defmacro define [list & body]
`(defn ~(first list) [~#(rest list)] ~#body))
Related
Just experimenting macros limitations or unknowns, I am not able make fixed len arity to symbol map with changed keys in run time.
So when k1 k2 are passed as args to process-arity, it should create j1 j2 symbols for accessing k1 and k2 values in syms-from-map macro.
Giving compile error:
CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.core$map
quoting unquoting makes difficult to write macros :-(
(defmacro syms-from-map[m]
`(let [~#(mapcat (fn[[k v]] [k v]) (var-get (resolve m)))]
(prn "got" ~'j1 ~'j2))
)
(defmacro process-arity[args]
`(let [] (fn ~args
(let [~'map1 (zipmap '[j1 j2] ~args)]
(syms-from-map ~'map1)
))))
(def test-m1 (process-arity [k1 k2]))
(apply test-m1 [1 2])
macroexpand-1 is a valuable tool. Use it.
(defmacro process-arity[args]
`(let [] (fn ~args
(let [~'map1 (zipmap [~'j1 ~'j2] ~args)]
(syms-from-map ~'map1)))))
(syms-from-map ~'map1) won't work the way you expect. I suggest you convert it to a function.
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've created a macro whose definition looks like this:
(defmacro my-macro
[strategy & body]
...)
You can call this macro like this:
(my-macro {:strategy "some"}
(do-something ..))
Now I'd like the macro to optionally accept bindings. Something like this:
(my-macro [my-value (load-value ..)]
{:strategy my-value}
(do-something ..))
So I've tried extending the macro to look like this:
(defmacro my-macro
([strategy & body] `(my-macro [] ~routes ~body))
([bindings strategy & body]
...))
But this fails with:
java.lang.RuntimeException: Can't have more than 1 variadic overload
Is there a good way to workaround this or what's the recommended approach?
My typical workaround would be to just examine the entire list of arguments passed to my-macro and decide whether to use bindings or not:
(defmacro my-macro
[& body]
(let [bindings (if (vector? (first body)) (first body) [])
[strategy body*] (if (seq bindings) (rest body) body)]
`(let ~bindings
(do-something-with ~body* ~strategy))))
i would propose this one:
(defmacro my-macro
{:arglists '([bindings? strategy & body])}
[bindings-or-strategy & more-args]
(let [[bindings strategy body] (if (vector? bindings-or-strategy)
[bindings-or-strategy
(first more-args)
(rest more-args)]
[[] bindings-or-strategy more-args])]
`(let ~bindings
(println :strategy ~strategy)
~#body)))
user> (my-macro [a 1] {:aaa :bbb} a)
:strategy {:aaa :bbb}
1
user> (my-macro {:aaa :bbb} 10)
:strategy {:aaa :bbb}
10
although it looks more verbose than #superkonduktr's answer, to me it seems to be more usable: it ensures that at least one argument is passed, and also documents the usage of this macro with :arglists
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.