Reputation: 2068
Let’s say that I want to define a macro called defsomething
such that this:
(defspecial a x)
…expands into this:
(def a (f `a x))
That syntax-quotation `a
there is the tricky part. I don’t see how I can attach the current namespace to the symbol properly so that it behaves like the syntax-quote. For instance:
(defmacro defspecial [var-symbol expr]
`(def ~var-symbol (f '~var-symbol ~expr)))
(defspecial a x)
…expands into:
(def a (f 'a x))
…which is close, but that second a
is not namespace-qualified.
I know I could use *ns*
:
(defmacro defspecial [var-symbol expr]
`(def ~var-symbol (f (symbol (name (ns-name *ns*)) (name '~var-symbol)) ~expr)))
…but I don’t like that; not only is it ugly, *ns*
is rebindable, so this would be possible:
(binding [*ns* (the-ns 'clojure.core)]
(defspecial a 5))
Unfortunately, Clojure apparently has no syntax-quote
form for `
like it has quote
for '
. And so, how should I best implement this macro, then?
Upvotes: 1
Views: 1958
Reputation: 1518
Here's a version that works for Clojure and ClojureScript:
(defn qualify-sym
[env s]
(if (:ns env)
;; cljs
(symbol (name (-> env :ns :name)) (name s))
;; clj
(symbol (str *ns*) (str s))))
(defmacro example [name val]
`(def ~name {:id '~(qualify-sym &env name) :value ~val}))
(example xxx 123)
xxx
;;=>
{:value 123, :id my-ns/xxx}
If you need to also take into consideration aliases and refered vars then this a more complete version:
(defn qualify-sym [env s]
(if (:ns env)
;; cljs
(if (simple-symbol? s)
(or (some-> env :ns :uses s name (symbol (name s)))
(symbol (name (-> env :ns :name)) (name s)))
(symbol (or (some-> env :ns :requires (get (symbol (namespace s))) name)
(namespace s))
(name s)))
;; clj
(if (simple-symbol? s)
(or (some-> (ns-refers *ns*) (get s) symbol)
(symbol (str *ns*) (str s)))
(let [ns (namespace s)
n (name s)
aliases (ns-aliases *ns*)]
(symbol (or (some-> aliases (get (symbol ns)) ns-name str) ns) n)))))
E.g. if you have (:require [clojure.string :as str])
, then qualifying str/join
will return clojure.string/join
Upvotes: 2
Reputation: 91857
Have you actually tried the last example you gave? The compiler's binding of *ns*
happens at compile-time, as does the macroexpansion, so the run-time binding you do should have no effect on the behavior of defspecial
.
Upvotes: 1