代码之家  ›  专栏  ›  技术社区  ›  Rob Lachlan

使用宏生成函数(或其他宏)时避免符号捕获

  •  4
  • Rob Lachlan  · 技术社区  · 16 年前

    我对Clojure宏何时捕获符号有点困惑。假设我有一个宏,它根据关键字定义了一个函数。在这个小例子中,

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

    我调用(foo:bar:baz),然后将其扩展为(defn bar[baz](*2 baz))。

    现在的问题是——这能导致符号捕获吗?如果是,在什么情况下? 我知道最好使用gensym(例如bar)来防止符号捕获,但在某些情况下(虽然不是很多,但仍然如此),我希望有一个相当大的扩展,没有自动生成的符号。

    附加问题:如果我们考虑创建宏的宏,答案会改变吗?

    1 回复  |  直到 16 年前
        1
  •  4
  •   kotarak    16 年前

    在您的示例中,不会发生符号捕获,因为将变量部分作为参数提供。所以开发者可以自己选择名字。

    当宏引入用户未指定的新局部变量时,将发生符号捕获。考虑下面的例子(真的很愚蠢和无稽之谈只是为了说明问题):

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

    在这种情况下,会捕获条。现在假设用户有这样的代码。

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

    这并不能达到预期的效果。因为全局栏,引用的用户会被宏引入的本地栏隐藏。正如你已经说过的:在这种情况下,应该使用bar来介绍本地。

    所以捕获总是用~'表示。宏编写宏并不能真正改变这一点。只需再添加一个级别:~~'。