【问题标题】:Avoiding symbol capture when using macros to generate functions (or other macros)使用宏生成函数(或其他宏)时避免符号捕获
【发布时间】:2010-05-28 05:10:27
【问题描述】:

对于 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#)来防止符号捕获,但在某些情况下(不是很多,但仍然)我希望有一个漂亮的宏扩展,没有自动生成的符号。

额外问题:如果我们正在考虑创建宏的宏,答案会改变吗?

【问题讨论】:

    标签: macros clojure symbol-capture


    【解决方案1】:

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

    当您的宏引入了用户未指定的新本地时,就会发生符号捕获。考虑以下示例(为了说明这一点,真的很愚蠢和荒谬):

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

    在这种情况下 bar 被捕获。现在假设用户有一些这样的代码。

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

    这不会给出预期的结果。因为全局条,引用的用户被宏引入的局部条所遮蔽。正如您已经说过的:在这种情况下,应该使用 bar# 来介绍本地。

    所以捕获总是用~'表示。宏编写宏并没有真正改变这一点。只需再添加一层:~~'。

    【讨论】:

    • 实际上 ~' 在您的示例中不会导致符号捕获。 (defmacro foo [name & body] `(defn ~name [bar] (println bar) ~@body)) 足以导致符号捕获,它可能取决于它之前或之后的代码导致运行时错误或编译时错误.
    • Jeremy Wall:您的示例将不起作用,因为一旦宏展开,bar 将成为命名空间限定,导致编译器出错,因为您无法绑定到命名空间限定符号。
    猜你喜欢
    • 2018-05-06
    • 2010-12-25
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    相关资源
    最近更新 更多