【问题标题】:Dealing with symbol resolution properly in macros在宏中正确处理符号解析
【发布时间】:2011-08-02 04:39:58
【问题描述】:

假设我想创建一个 Clojure 宏来执行以下操作:

If x is a list calling the function "bar"
  return :foobar
else
  return x as a string

但是,bar 没有定义;相反,它只在宏内部使用,如下所示:

(foo (bar))
:foobar

(foo 1)
"1"

可以这样做:

(defmacro foo [x] 
  (if (and (coll? x) (= (first x) 'bar)) 
      :foobar 
      (str x)))

这适用于(bar) 情况以及文字。但是,符号不能按预期工作,给出符号名称而不是其关联值:

user=> (def y 2)
#'user/y
user=> (foo y)
"y"

在将x 传递给str 之前,可以调用eval 函数,但这会在使用let 中的函数时出现问题:

user=> (let [a 3 b (foo a)] b)
java.lang.UnsupportedOperationException: Can't eval locals (NO_SOURCE_FILE:89)

大概,问题与符号解析有关,所以也许我们尝试用语法引用解决一些问题:

(defmacro foo [x] 
  `(if (and (coll? '~x) (= (first '~x) '~'bar)) 
    :foobar 
    (str ~x)))

现在,问题在于(foo (bar)),因为这会将else 子句扩展为(clojure.core/str (bar)),这会引发异常,因为bar 未定义。然后我尝试用eval做一些恶作剧:

(defmacro foo [x] 
  `(if (and (coll? '~x) (= (first '~x) '~'bar)) 
    :foobar 
    (eval '(str ~x))))

但这又不适用于let 绑定:

user=> (let [a 1 b (foo a)] b)
java.lang.Exception: Unable to resolve symbol: a in this context (NO_SOURCE_FILE:153)

所以我在这里真的很茫然。似乎解决一个问题会破坏另一个问题。有没有更好、更简单的方法来制作这个宏,使其在以下情况下工作:

  • let 绑定中
  • (bar)
  • 带符号

附:如果有人对为什么我想这样做感到好奇,我正在为 Yahoo 的 YQL 服务开发 DSL,我希望能够做类似 (select (table :t) ...) 这样的事情,但我需要能够传入符号以及文字。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    我相信这应该可行。

    (defmacro foo [x]
      (if (and (coll? x) (= (first x) 'bar))
        :foobar
        `(str ~x)))
    

    【讨论】:

    • 我想我没有考虑仅对表达式的一部分进行语法引用,但这似乎可以做到。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    • 2019-06-25
    • 2019-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-05
    相关资源
    最近更新 更多