2011-08-01 13 views
5

说我想要做的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" 

人们可以将它传递给str之前呼吁xeval功能,但是这会导致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) ...)这样的事情,但我需要能够传递符号,如以及文字。

回答

4

我相信这应该工作。

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

我想我没有考虑语法引用只是表达式的一部分,但似乎这样做。谢谢! – Scott