Clojure宏作为函数/'部分'用于宏?

时间:2012-09-25 18:37:41

标签: clojure macros partial

这类似于Treat Clojure macro as a function中讨论的问题,但在最佳答案中尝试方法时,我收到了错误。希望有太多关于我的特定应用程序的信息是没有必要的,因为它非常复杂,但这是我试图做的简化版本:

(defmacro make-fn [m arg1] 
    `(fn [& args#] 
      (eval `(~'~m ~'~arg1 ~@args#))))

我在这个上下文中使用了宏:

(let [columns (make-columns table-width)
       table-name (keyword (str "table_" n))]
  (apply (make-fn helpers/tbl table-name) columns))

“helpers / tbl”是一个宏,它需要一个表名关键字和一个包含列规范的可变数量的列表(如[:varchar 100]或其他东西)。我正在尝试动态创建随机数据库表规范以方便一些测试。无论如何,当我尝试执行上面的代码时,我收到以下错误:

CompilerException java.lang.RuntimeException: Unable to resolve symbol: table-name in this context, compiling:(NO_SOURCE_PATH:1) 

我有点理解这个问题:宏扩展是在编译时完成的,我试图在宏扩展中包含一个运行时值,因此奇怪的使用引用和取消引用来使所有内容都设置得恰到好处。我基本上想要部分用于宏,并且我需要能够将这种机制重用于不同命名空间中的不同宏,并且所有的变量分辨率都是正确的。这甚至可能吗?

1 个答案:

答案 0 :(得分:2)

问题是由Clojure在语法引用(反引号)表达式中解析符号的方式引起的。为了避免无意的变量捕获,Clojure总是将语法引用表达式中的符号解释为引用Vars( not locals)。

你可以通过“滚动你自己的”表单构建代码来解决这个问题,相当于syntax-quote生成的代码。它像罪一样丑陋,但它有效......只是不要说我没有警告你:

(defmacro make-fn [m arg1]
  (let [g (gensym)]
    (list 'fn ['& g]
      (list 'eval (list 'concat (list 'list m arg1) g)))))
哇,这就像是我的Common Lisp时代的闪回......