How do I create a macro to define two functions in clojure - macros

The code below doesn't behave as I would expect.
; given a function name, its args and body, create 2 versions:
; i.e., (double-it foo []) should create 2 functions: foo and foo*
(defmacro double-it
[fname args & body]
`(defn ~fname ~args ~#body)
`(defn ~(symbol (str fname "*")) ~args ~#body))
The code above doesn't create two functions as I would expect. It only creates the last one.
user=> (double-it deez [a b] (str b a))
#'user/deez*
How can I get a single macro to define two functions?

; given a function name, its args and body, create 2 versions:
; ie (double-it foo [] ) should create 2 functions: foo and foo*
(defmacro double-it
[fname args & body]
`(do (defn ~fname ~args ~#body)
(defn ~(symbol (str fname "*")) ~args ~#body)))
(double-it afunc [str] (println str))
(afunc* "asd")
(afunc "asd")
No need to quote them separately.

Related

Clojure macro inside another macro: how to generate string from symbol

Probably the title is not 100% correct, but let me show you the issue:
(defmacro nvp!
[n width height]
`(q/defsketch (symbol (str (name '~n) "-viewport" ))
:title (name '~n))
In short: there's a macro called defsketch (it's part of the lib quil, but that does not matter). It's signature is basically defsketch [applet-name & options], and it creates something and binds it to a var called applet-name. For some reason, I want to wrap this into another macro that -- among other things -- takes an applet-name parameter, extends that name into applet-name-viewport and passes that to defsketch. I, however, am unable to figure out how to do that correctly (using macroexpand on the code above ((pprint (macroexpand(nvp test-name 500 500))), I get
(def(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport"))
(quil.applet/applet
:title (clojure.core/name 'my-namespace.core/test-name)))
(clojure.core/symbol (clojure.core/str (clojure.core/name 'my-namespace.core/test-name) "-viewport") -- this part looks good, but it should be evaluated somehow before passing it to the inner macro...
You need to unquote the form that generates the new symbol instead of the original symbol itself.
Here is a small example of how to accomplish this using a defn like macro for the inner macro:
(defmacro mydefn
[name & body]
`(defn ~name ~#body))
(defmacro defnview
[n & body]
`(mydefn ~(symbol (str (name n) "-viewport")) ~#body))
;; OR
(defmacro defnview
[n & body]
(let [n (symbol (str (name n) "-viewport"))]
`(mydefn ~n ~#body)))
Example:
(defnview foo [a] a)
;; => #'user/foo-viewport
(foo-viewport 1)
;; => 1
(macroexpand '(defnview foo [a] a))
;; => (def foo-viewport (clojure.core/fn ([a] a)))

How to add docstring support to defn-like Clojure macro?

I wrote a macro to wrap a function definition in some helpful logging:
(defmacro defn-logged
"Wraps functions in logging of input and output"
[fn-name args & body]
`(defn ~fn-name ~args
(log/info '~fn-name "input:" ~#args)
(let [return# (do ~#body)]
(log/info '~fn-name "output:" return#)
return#)))
This works great for functions without docstrings:
(defn-logged foo
[x]
(* 2 x))
(foo 3)
; INFO - foo input: 3
; INFO - foo output: 6
; 6
But if fails terribly for functions with docstrings:
(defn-logged bar
"bar doubles its input"
[x]
(* 2 x))
; IllegalArgumentException Parameter declaration clojure.tools.logging/info should be a vector
How do I make my macro work for functions both with and without docstrings?
One way to do it is to look at the arguments passed to defn-logged. If the first one after the name is a string use that as doc string, otherwise leave doc empty:
(defmacro defn-logged
"Wraps functions in logging of input and output"
[fn-name & stuff]
(let [has-doc (string? (first stuff))
doc-string (if has-doc (first stuff))
[args & body] (if has-doc (rest stuff) stuff)]
`(defn ~fn-name {:doc ~doc-string} ~args
(println '~fn-name "input:" ~#args)
(let [return# (do ~#body)]
(println '~fn-name "output:" return#)
return#))))
Test with doc string:
(defn-logged my-plus "My plus documented" [x y] (+ x y))
(doc my-plus)
; -------------------------
; user/my-plus
; ([x y])
; My plus documented
; nil
(my-plus 2 3)
; my-plus input: 2 3
; my-plus output: 5
; 5
Test without doc string:
(defn-logged my-mult [x y] (* x y))
(doc my-mult)
; -------------------------
; user/my-mult
; ([x y])
; nil
; nil
(my-plus 2 3)
; my-mult input: 2 3
; my-mult output: 6
; 6
It still is not a complete equivalent of defn, at least because defn supports metadata passed in map, reader macro and string. But it works for doc strings.
I defined a generic function for identifying the parameters in this scenario.
(defn resolve-params
"Takes parameters to a def* macro, allowing an optional docstring by sorting
out which parameter is which.
Returns the params, body, and docstring it found."
[args]
(if (string? (first args))
[(second args) (drop 2 args) (first args)]
[(first args) (rest args)]))
Then your macro definition becomes as simple as this:
(defmacro my-macro
[fn-name & args]
(let [[params body docstring] (resolve-params args)]
~your-macro-here))

Clojure macro to process multiple function metadata

In Clojure, how do I make a library macro which processes supplied functions metadata and return some result? Amount of functions is unlimited and they should be passed without being boxed into a sequence ((my-macro fn1 fn2) instead of (my-macro [fn1 fn2]))
Say, we expect function vars having :foo keys in meta and the macro concatenates their values. The following snippet should work in REPL (considering my-macro is in the namespace):
user=> (defn my-func-1 {:foo "bar"} [])
(defn my-func-1 {:foo "bar"} [])
#'user/my-func-1
user=> (defn my-func-2 {:foo "baz"} [])
(defn my-func-2 {:foo "baz"} [])
#'user/my-func-2
user=> (my-macro my-func-1 my-func2)
(my-macro my-func-1 my-func2)
"barbaz"
I tried several approaches but was only able to process single function so far.
Thanks!
Try this:
(defmacro my-macro [& fns]
`(clojure.string/join (list ~#(map (fn [x] `(:foo (meta (var ~x)))) fns))))
(defn ^{:foo "bar"} my-func-1 [])
(defn ^{:foo "baz"} my-func-2 [])
(my-macro my-func-1 my-func-2) ;; => "barbaz"
How it Works
If you expand the macro you can start to see the parts in play.
(macroexpand '(my-macro my-func-1 my-func-2))
(clojure.string/join
(clojure.core/list (:foo (clojure.core/meta (var my-func-1)))
(:foo (clojure.core/meta (var my-func-2)))))
(var my-func-1)
Function metadata is stored on the var, so using (meta my-func-1) is not sufficient. But, var is a special form and does not compose like a normal function.
(fn [x] `(:foo (meta (var ~x))))
This anonymous function exists inside an escaped form, so it is processed inside the macro to produce the output forms. Internally it will create a the (:foo (meta (var my-func-1))) form by first backtick escaping the outer form to declare it a literal, and not evaluated, list and then unescaping the x var with a tilde to output the value instead of the symbol.
`(clojure.string/join (list ~#(map (fn [x] `(:foo (meta (var ~x))))
fns)))
This entire form is backtick escaped, so it will be returned literally. But, I still need to evaluate the map function generating the (:foo (meta (var my-func-1))) form. In this case I have unescaped, and spliced (#) the result of, the map form directly. This first evaluates the map function and returns a list of generated forms, and then takes the contents of that list and splices it into the parent list.
(defmacro test1 [x] `(~x))
(defmacro test2 [x] `(~#x))
(macroexpand '(test1 (1 2 3))) ;; => ((1 2 3))
(macroexpand '(test2 (1 2 3))) ;; => (1 2 3)
You could also split out the map function in a let statement before hand for slightly more readability.
(defmacro my-macro [& fns]
(let [metacalls (map (fn [x] `(:foo (meta (var ~x)))) fns)]
`(clojure.string/join (list ~#metacalls))))

Why is my macro's form not evaluated before function invocation?

I have the following Clojure code:
(defn mul [a b]
(* a b))
(defmacro create-my-macro [macroname]
`(defmacro ~macroname [a# b#]
(mul a# b#)))
(create-my-macro my-mul)
(my-mul 1 2)
;; => 2
(my-mul (+ 1 1) 2)
;; => ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number
I get the answer I want when I wrap the arguments in the invocation of mul with eval:
(mul (eval #a) (eval #b))
But I don't understand why it is necessary to do that: if the my-mul macro had been defined directly (and not through another macro), it would work. For example, the following works fine:
(defmacro my-mul [a b] `(mul ~a ~b))
(my-mul (+ 1 1) 2)
;; => 4
Why am I seeing this behavior?
Edit: In response to a comment, below are the macroexpands for the failing case (i.e., without using eval):
(macroexpand '(create-my-macro my-mul))
;; => (do
;; (clojure.core/defn my-mul
;; ([&form &env a__58__auto__ b__59__auto__]
;; (foo/mul a__58__auto__ b__59__auto__)))
;; (. (var my-mul) (setMacro)) (var my-mul))
(macroexpand '(my-mul (+ 1 1) 2))
;; => ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number clojure.lang.Numbers.multiply (Numbers.java:146)
As you show, the code you'd like to emit looks like:
(defmacro my-mul [a b] `(mul ~a ~b))
So you need to syntax-quote it and suffix all locals with #:
`(defmacro my-mul [a# b#] `(mul ~a# ~b#))
Thus you macro-emitting macros should be:
(defmacro create-my-macro [macroname]
`(defmacro ~macroname [a# b#]
`(mul ~a# ~b#)))
(defmacro create-my-macro [macroname]
`(defmacro ~macroname [a# b#]
`(mul ~a# ~b#)))

Clojure macro throws "CompilerException java.lang.IllegalStateException: Var clojure.core/unquote is unbound" when called

I'm trying to write a clojure macro that lets me call a function and retrieve the arguments from a map/struct using the provided key values such as:
(with-params {:apple 2 :banana 3 :cherry 7} + :apple :banana)
;; 5
but when I try to use the macro that I wrote:
(defmacro with-params [s f & symbols]
`(~f ~#(map ~s ~symbols)))
calling
(with-params {:apple 2 :banana 3 :cherry 7} + :apple :banana)
gives me
#<CompilerException java.lang.IllegalStateException: Var clojure.core/unquote is unbound. (NO_SOURCE_FILE:0)>
Could someone help me understand how syntax-quoting works here?
For what it's worth, this shouldn't be a macro anyway. A function is plenty powerful, and the macro version will only work if both the map and the keywords are given as compile-time literals.
(defmacro with-params-macro [s f & symbols]
`(~f ~#(map s symbols)))
(defn with-params-fn [s f & symbols]
(apply f (map s symbols)))
user> (with-params-macro {:x 1} (fn [z] z) :x)
1
user> (let [params {:x 1}]
(with-params-macro params (fn [z] z) :x))
nil
user> (let [params {:x 1}]
(with-params-fn params (fn [z] z) :x))
1
The reason that `(~f ~#(map ~s ~symbols)) doesn't work is that the compiler chokes on the unnecessary unquote (~) inside the unquote-splicing (~#). The unquote-splicing unquotes the outer syntax-quote (`), so the inner two unquotes don't have any matching syntax-quote, which is why you were getting the "unbound" error.
What you want to do is evaluate (map s symbols) first to get the sequence of operands, then pass the flattened result to the function (~f); therefore the correct version is:
(defmacro with-params [s f & symbols] `(~f ~#(map s symbols)))
You can easily verify this with:
(macroexpand '(with-params {:a 1 :b 2 :c 5} * :a :b :c)) ;; (* 1 2 5)
(with-params {:a 1 :b 2 :c 5} * :a :b :c) ;; 10