开发者

Avoiding symbol capture when using macros to generate functions (or other macros)

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 so开发者_JAVA技巧me 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?


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: ~~'.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜