2

I have a macro as such:

Example:

(defmacro xxz [& fns] `(:body ~@(map (fn [[e1 e2]] `(~e2 "http://www.google.com")) fns)))

If I pass something like (xxz [client/get client/get]), the resulting macroexpand shows that the symbols weren't qualified:

(:body (client/get "http://www.google.com"))

This causes problems when something like this lands in a namespace that doesn't have client imported.

Does anybody know what to do?

1
  • Not sure I understand. If you are in ns "sample" and you call (xxz ...), then ns "sample" must have (:require [some-ns.client :as client) or similar. Commented Oct 20, 2016 at 18:34

1 Answer 1

2

This sounds like a design oversight with syntax-quote (in my opinion), though it's not clear what a general "fix" would look like.

here is a more minimal example:

yummly.mobile-api.main> (in-ns 'foo)
#namespace[foo]
foo> (clojure.core/refer-clojure)
foo> (require '[org.httpkit.client :as client])
nil

foo> (defmacro xxz [& fns]
       `(~@fns))
#'foo/xxz
foo> (macroexpand-1 '(xxz client/get))
(client/get)

from a new namespace:

foo> (in-ns 'bar)
#namespace[bar]
bar> (macroexpand-1 '(foo/xxz client/get))
(client/get)
bar> (foo/xxz client/get)
CompilerException java.lang.RuntimeException: No such namespace: client, compiling:(*cider-repl api*:87:6) 

the syntax-quote-form (aka `) looks at every symbol in the expression as it's being compiled and if it does not find a / then it assumes it's for the local namespace and appends the current namespace. It can only do this for symbols that are present in the actual macro at the time the macro is defined, not (as I would like it to be able to do) for symbols that are passed as arguments to the macro.

If you use fully namespace qualified symbols in your macro then you don't need to worry if namepace where people use your macro has these symbols mapped to anything, because they spell out the full path to the symbol in it's name. If you don't want to actually type the full namespace in the symbols you use in your macro, then you can use :refer [get] in the require statement in your ns expression, this will save you having to type out the name and cause them to be correctly namespace expanded at the time that the macro definition is evaluated.

Because syntax quote is producing unhygenic symbols, you will need to either both :require or :refer to the functions in your ns section at the top of the namespace for every client where these namespaces are passed to the function.

Another option is to find some way to make sure the symbol is available to the macro at the time the syntax-quote is compiled. This can be tricky in some cases.

Sign up to request clarification or add additional context in comments.

4 Comments

Since you are using namespace qualified symbols in your macro (as you should) then you don't need to worry if namepace where people use your macro has these symbols mapped to anything, because they spell out the full path to the symbol in it's name. -> this is exactly the problem. For some reason in this case I do not get the full path to the symbol. I get client/get symbol in macroexpand instead of clj-http.core/get which is the correct fully qualified symbol. Hence my question: why doesn't it get qualified?
That really sounds like a bug in syntax quote. It's not supposed to work that way
I think this should be added to the Clojure Jira issue tracker
I'll try to create a sample reproducing the bug.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.