4
votes

I'm a bit confused as to exactly when symbol capture will occur with clojure macros. Suppose that I have a macro which defines a function from keywords. In this trivial example,

(defmacro foo [keywd1 keywd2] `(defn ~(symbol (name keywd1)) 
                  [~(symbol (name keywd2))] (* 2 ~(symbol (name keywd2))))) 

I call (foo :bar :baz), and this gets expanded into (defn bar [baz] (* 2 baz)).

So now the question -- can this lead to symbol capture? If so, under what circumstances? I know that it's preferred to use gensym (e.g. bar#) to prevent symbol capture, but in some cases (not many, but still) I'd like to have a pretty macro-expansion, without the auto-generated symbols.

Bonus question: does the answer change if we are considering a macro that creates macros?

1

1 Answers

4
votes

In your example symbol capture does not happen, because provide the variable parts as parameters. So the developer can choose the names himself.

Symbol capture happens when your macro introduces new locals which are not specified by the user. Consider the following (really silly and nonsensical just to show the point) example:

(defmacro foo
  [name & body]
  `(defn ~name
     [~'bar]
     (println ~'bar)
     ~@body))

In this case bar is captured. Now suppose the user has some code like this.

(def bar 5)
(foo baz (* 2 bar))
(baz 7)

This would not give what the would expect. Because the global bar, the user refering to gets shadowed by the local bar introduced by the macro. As you already said: in this case one should use bar# to introduce the local.

So capture is always indicated by ~'. Macro writing macros don't really change that. Just add one more level: ~~'.