如何在Clojure宏中进行命名空间解析

时间:2016-10-20 18:20:19

标签: clojure

我有一个宏:

示例:

window.onload = function () {
    setTimeout(function(){
        manualTime();
    ),1000);
}

window.onload = function () {
    setInterval(function(){
        solveAll();
    ),60000);
}

如果我传递(defmacro xxz [& fns] `(:body ~@(map (fn [[e1 e2]] `(~e2 "http://www.google.com")) fns))) 之类的内容,则生成的宏扩展显示符号不合格:

(xxz [client/get client/get])

当这样的东西落入没有导入(:body (client/get "http://www.google.com"))的命名空间时,会导致问题。

有人知道该怎么办吗?

1 个答案:

答案 0 :(得分: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.