I want a macro to define functions that return the form they were called as, e.g. (func 1 (a b))
returns (func 1 (a b))
. I also want to allow input verification for these functions to make sure I'm not introducing any bugs. (These forms will later be evaluated, but that code hasn't been written yet.)
I keep getting this error, though.
(defmacro defecho
"Echo function call after asserting a few things about the input"
([f] `(defecho ~f nil nil))
([f assertions] `(defecho ~f assertions nil))
([f assertions assert-failed-message]
`(defn ~f [& body] ; define a function
~(when-not (nil? assertions) ; if given a function for input validation
`(assert (~assertions body) ; define the function to assert this as true
~assert-failed-message)) ; with a given error message
(conj body (quote ~f))))) ; return the (f ~@body) list
(defecho my-test
#(< 2 (count %))
"Must be greater than zero")
Unhandled clojure.lang.Compiler$CompilerException
Error compiling:
/private/var/...228.clj:1:1
Can't use qualified name as parameter: my-test/body
Caused by java.lang.RuntimeException
Can't use qualified name as parameter: my-test/body
You can't use qualified symbols as function parameters. Observe that
`body
evaluates to current-namespace/body
In a syntax-quote, you can always unquote a non-syntactic quote to get an unqualified symbol:
`~'body
evaluates to body
. (Notice that unquoting here serves evaluation of the inner quoting itself).
However, in this case you should generate a symbol instead, because if a user uses the symbol body
within e. g the code of assert-failed-message
you don't want his binding of body
to be shadowed with yours (observe that his code is evaluated when the generated function is actually called).
It is common practice to generate symbols for that purpose, either using gensym
or a symbol ending with a hash, which syntax quote will expand to a gensym call..
`body#
evaluates to the (unqualified!) symbol body__34343__auto__
where the number differs on each invocation and is guaranteed to be different each time.
Since you refer to body from within two different syntax quotes, I picked the gensym
option in combination with let
so that only one symbol is generated.
(defmacro defecho ; overloads stripped for brevity
[f assertions assert-failed-message]
(let [args-sym (gensym "body")] ; define a symbol for function arglist
`(defn ~f [& ~args-sym] ; define a function
~(when-not (nil? assertions) ; if given a function for input validation
`(assert (apply ~assertions ~args-sym) ; define the function to assert this as true
~assert-failed-message)) ; with a given error message
(conj ~args-sym (quote ~f)))))
You can make your life a little simpler by using a proper function for the heavy lifting and use the macro only for the syntax sugar:
(defmacro defecho
"Echo function call after asserting a few things about the input"
([f] `(defecho ~f nil nil))
([f assertions] `(defecho ~f assertions nil))
([f assertions assert-failed-message]
`(def ~f (echo-function (quote ~f) ~assertions ~assert-failed-message))))
(defn echo-function [f assertion assert-failed-message]
(fn [& body]
(when assertion
(assert (assertion body)
assert-failed-message))
(conj body f)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With