I'm trying to write a portable version for thrown?. I wrote something like this:
(defmacro throws? [body]
`(~is
(~thrown?
#?(:jank cpp/std.runtime_error
:cljs js/Error
:default Exception)
~body)))
But it doesn't even compile with the following error:
Syntax error compiling at (REPL:1:1).
Can't take value of a macro: #'clojure.test/is
I wanted to resolve the is & thrown? symbols in the scope of the macro definition but adding ~ in front of them doesn't work.is is a clojure.test macro so you need the fully-qualified symbol there, but thrown? is special syntax for is and needs to be a literal symbol:
user=> (defmacro throws? [body]
#_=> `(clojure.test/is
#_=> ('~'thrown?
#_=> #?(:jank cpp/std.runtime_error
#_=> :cljs js/Error
#_=> :default Exception)
#_=> ~body)))
#'user/throws?
user=> (throws? (/ 1 0))
ERROR in () (Numbers.java:190)
expected: ((quote thrown?) java.lang.Exception (/ 1 0))
actual: java.lang.ArithmeticException: Divide by zero(or if you have :refer [is] then you can just have (is ('~'thrown? ..)))
> or if you have :refer [is]
Yes to this.
Is my understanding correct for this part (`'~'thrown?`)?
• 'thrown? -> Gets you the symbol called thrown?.
• ~'thrown? -> Resolve it in the scope of the macro definition (the "fully-qualified symbol" bit you mentioned).
• '~'thrown? -> Once fully qualified we quote it to prevent the evaluation, since we just need it to be a fully qualified symbol and not an actual var.
No, thrown? is not fully-qualified, '~'sym produces the literal, unqualified sym in the expansion:
user=> (defn foo [])
#'user/foo
user=> (defmacro bar [] `(println foo 'foo ~'foo '~'foo))
#'user/bar
user=> (bar)
#function[user/foo] user/foo #function[user/foo] fooAh ok, thanks for the explanation!
'~' is quote unquote quote, which is too many here, you'll want unquote quote ~' , quote thrown? to get the symbol, unquote to splice the symbol in
~' is the kind of macro stuff that makes my head hurt
All macro stuff hurts mine!