【问题标题】:Clojure macro as function / 'Partial' for Macros?Clojure 宏作为函数/宏的“部分”?
【发布时间】:2012-09-25 18:37:41
【问题描述】:

这类似于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) 

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

【问题讨论】:

    标签: clojure macros partial


    【解决方案1】:

    问题是由 Clojure 解析语法引号(反引号)表达式中的符号的方式引起的。为避免意外捕获变量,Clojure 始终将语法引用表达式中的符号解释为引用 Vars(not 局部变量)。

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

    (defmacro make-fn [m arg1]
      (let [g (gensym)]
        (list 'fn ['& g]
          (list 'eval (list 'concat (list 'list m arg1) g)))))
    

    哇,这就像回到我的 Common Lisp 时代......

    【讨论】:

    • 顺便说一句,关于syntax-quote 的有趣事实:它完全在阅读器中实现。当阅读器看到反引号时,它会读取以下形式,然后将其递归地转换为使用seqconcatlist 的代码。编译器只看到生成的concat/list 代码。
    猜你喜欢
    • 2011-07-28
    • 2018-11-19
    • 2011-12-12
    • 2011-08-09
    • 2019-11-07
    • 1970-01-01
    • 2010-11-21
    • 2018-05-26
    相关资源
    最近更新 更多