Namespaced anaphors in Clojure?

Suppose I have a macro, let’s call it aif, and within the macro I want people to be able to use the name it. Here’s how I might write such a macro in Clojure (note that this is only an example. if-let is probably a better thing to use in this particular use-case. Also note that I did not test this code).

(defmacro aif [test then else]

`(let [~'it ~test]

(if ~'it ~then ~else)))

This code is usable as such:

(aif (+ 2 2) (println it) (println "false"))

Within the forms provided to aif, a lexically bound variable “it” is visible. Within this post, I shall call things similar to “it” an anaphor.

Note how within the quasiquote (`) I use ~’ to prevent it from being expanded into some-namespace/it. let will only accept non-namespaced symbols, such as it, rather than a symbol such as sandbox/it or clojure.core/it, and normally, with quasiquote, bare symbols will get a namespace stuck onto them. The ~’ will prevent the namespace from being stuck on.

But suppose I have two anaphoric macros that I want to use together, that both expose an anaphor with the same name. There is no way to choose between them. It is also at times counterintuitive, if I’m exposing a function as an anaphor, that I don’t use that function the way that I would use any other function that a namespace provides.

One solution to this might be to not use anaphoric macros of this form (other anaphoric macros that do things other than provide a few names to use within their bodies are not under discussion in this post). For example, aif could be written differently, so as to accept a name. clojure.core includes such a macro, called if-let. However, as pointed out to me by amalloy on IRC, this could make some macros inconvenient to use. recur is, in a sense, an anaphor of loop.

A second solution, the one that I’m proposing, is to allow let to accept namespaced symbols. The form would still only be lexically binding, but could mean that the anaphor would be treated just like any other namespaced symbol. Suppose I’m written my aif macro in the aif namespace. So let’s say, instead of using `(let [~‘it ~test] …) I used `(let [it ~test] …) or `(let [aif/it ~test]) (the latter two are equivalent). Then, users might use it as aif/it, or they may (require ’[aif :as abc]) and then, within the macro, use abc/it. Or they may (use 'aif), in which case they use it as before.