2011-08-01 70 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

相關問題