We were attempting to write functions could round-trip function references to strings and, though we eventually figured this out in a different way, our first attempts brought a very confusing error. It seems to have something to do with how var works, and some underlying characteristic of Clojure that I would love to understand.
We were adapting this macro, which worked in the REPL: (defmacro fn-name [f] `(-> ~f var meta :name str)) . But it failed when written as a larger fn, below, and even with macro-expand1 it wasn’t clear what was going wrong, although trial and error indicated that it has something do with with how var works. Can anyone shed some light on the nature of the problem?
(defn foo [] nil)
(defmacro parse-fn->string_fails
"Should parse a function, like `clojure.core/str`, to a string \"clojure.core/str\" "
[f]
`(let [m (-> ~f var meta)
fname (:name m)
fns (:ns m)]
(str fns "/" fname)))
(parse-fn->string_fails foo)
;; =>
;; Call to clojure.core/let did not conform to spec.
;; #:clojure.spec.alpha{:problems ({:path [:bindings :form :local-symbol], :pred clojure.core/simple-symbol?, :val logging.core/m, :via [:clojure.core.specs.alpha/bindings :clojure.core.specs.alpha/bindings ...
Which works. In the fn-name macro, you didn’t have this problem since you didn’t have a let binding with symbols. In the new macro, without gensyms, the unquoted symbols in the let were expanded into ns-qualified symbols, which were unable to resolve and triggered errors during evaluation of the let form. With the gensyms generating valid unqualified symbols for let, you now have valid bindings and the macro works.