提前编译时,Clojure fn 名称会泄漏到其范围之外
Clojure fn name leaking outside its scope when compiled ahead-of-time
我想用 fn 和 return 从宏中生成命名函数,我尝试了以下示例:
(defmacro getfn
[namestr children]
`(fn fn-name# []
(println "Recursing" ~namestr)
(doall (map (fn [child#] (child#)) ~children))))
(def foo (getfn "foo" []))
(def bar (getfn "bar" [foo]))
(defn -main [& args]
(bar))
结果输出通常符合预期:
Recursing bar
Recursing foo
但是,当我 运行 这个提前编译 (AOT) 时,我得到:
Recursing bar
Recursing bar
...
Recursing bar
Recursing bar
Exception in thread "main" java.lang.WhosebugError
我觉得 bar 一直调用自己而不是 foo 很奇怪,唯一合理的原因是生成的符号 fn-name#
泄漏到它的范围之外。这是 Clojure 中的错误还是预期的行为?
更新: 为清楚起见,应提及删除 fn-name#
符号并使函数匿名可解决此问题。但是,在我的实际代码中,有时需要递归调用,所以命名是必要的。
我对这个问题的一个解决方案是使用 gensym 为宏的每个版本获取一个新符号,这可以通过如下修改 getfn 来实现:
(defmacro getfn
[namestr children]
`(let [fn-name# (gensym)]
(fn fn-name# []
(println "Recursing" ~namestr)
(doall (map (fn [child#] (child#)) ~children)))))
这感觉有点不必要,因为根据定义,fn 名称应该只在其自己的范围内相关。
更新: 刚刚测试了 alpha 版本,似乎 Clojure 1.7.0-alpha3 和更高版本在没有这个 hack 的情况下工作,Clojure 1.7.0-alpha2 和更早版本被破坏了。在 1.7.0 的稳定版本发布之前,使用此解决方法可能没问题,除非有人能想到更好的方法。
我想用 fn 和 return 从宏中生成命名函数,我尝试了以下示例:
(defmacro getfn
[namestr children]
`(fn fn-name# []
(println "Recursing" ~namestr)
(doall (map (fn [child#] (child#)) ~children))))
(def foo (getfn "foo" []))
(def bar (getfn "bar" [foo]))
(defn -main [& args]
(bar))
结果输出通常符合预期:
Recursing bar
Recursing foo
但是,当我 运行 这个提前编译 (AOT) 时,我得到:
Recursing bar
Recursing bar
...
Recursing bar
Recursing bar
Exception in thread "main" java.lang.WhosebugError
我觉得 bar 一直调用自己而不是 foo 很奇怪,唯一合理的原因是生成的符号 fn-name#
泄漏到它的范围之外。这是 Clojure 中的错误还是预期的行为?
更新: 为清楚起见,应提及删除 fn-name#
符号并使函数匿名可解决此问题。但是,在我的实际代码中,有时需要递归调用,所以命名是必要的。
我对这个问题的一个解决方案是使用 gensym 为宏的每个版本获取一个新符号,这可以通过如下修改 getfn 来实现:
(defmacro getfn
[namestr children]
`(let [fn-name# (gensym)]
(fn fn-name# []
(println "Recursing" ~namestr)
(doall (map (fn [child#] (child#)) ~children)))))
这感觉有点不必要,因为根据定义,fn 名称应该只在其自己的范围内相关。
更新: 刚刚测试了 alpha 版本,似乎 Clojure 1.7.0-alpha3 和更高版本在没有这个 hack 的情况下工作,Clojure 1.7.0-alpha2 和更早版本被破坏了。在 1.7.0 的稳定版本发布之前,使用此解决方法可能没问题,除非有人能想到更好的方法。