2012-10-29 22 views
6

try在一個宏中,catch在第二個由第一個調用的宏中。如何讓以下工作?可以嘗試捕捉不同的(但是嵌套的)宏嗎?

(defmacro catch-me [] 
    `(catch ~'Exception ~'ex 
    true)) 

(defmacro try-me [] 
    `(try (+ 4 3) 
     (catch-me))) 

擴大try-me看起來不錯:

(clojure.walk/macroexpand-all '(try-me)) 

產生

(try (clojure.core/+ 4 3) (catch Exception ex true)) 

但調用(試行-ME)產量:

"Unable to resolve symbol: catch in this context", 

其中,順便說一句,也是你會在REPL中獲得的消息在不嘗試的時候使用catch。

UPDATE:

這是我如何能得到它的工作(感謝,@Barmar),在這裏你可以看到我的代碼的實際背景:

(defmacro try-me [& body] 
    `(try 
    [email protected] 
    [email protected](for [[e msg] [[com.mongodb.MongoException$Network "Database unreachable."] 
         [com.mongodb.MongoException "Database problem."] 
         [Exception "Unknown error."]]] 
     `(catch ~e ~'ex 
      (common/site-layout 
      [:div {:id "errormessage"} 
       [:p ~msg] 
       [:p "Error is: " ~e] 
       [:p "Message is " ~'ex]]))))) 

,但是這是我希望(使用單獨的宏catch-me):

(defmacro try-me [& body] 
    `(try 
    [email protected] 
    (catch-me com.mongodb.MongoException$Network "Database unreachable.") 
    (catch-me com.mongodb.MongoException "Database problem.") 
    (catch-me Exception "Unknown error."))) 

我認爲這將是更容易編寫/維護。

任何想法?我需要語法引用,因爲我傳遞參數,這就是爲什麼不幸的是,Arthur的答案不能被應用(或者它可以以某種方式?),但是直到現在我才發佈我的實際上下文。

+0

我目前的猜測是,(你追我趕)首先擴大,然後很明顯,它是在一個嘗試。是嗎?如何戰鬥呢? – 0dB

+1

只有在正常評估表達式的地方纔會擴展宏。 'catch'子表單不是被評估的表達式,它們是'try'語法的一部分。 – Barmar

+0

我目前仍然認爲這個問題沒有答案。令人困惑的是,爲什麼Arthur的例子能夠工作,但是使用語法引用的卻沒有,而且,這個macroexpand-all顯示了一個正在運行的宏擴展,但直接調用它卻失敗並報錯。感謝Barmar提供的解決方法。任何其他想法,任何人? – 0dB

回答

5

的原因,你得到這個錯誤是因爲try語法是:

(try expr* catch-clause* finally-clause?) 

這意味着可以爲最後條款之前在任意數量的EXPR形式。 try掃描expr s直至找到以catchfinally開頭的一個。它在之前做了這個擴展任何宏,因爲它只是試圖找出exprs和catch/finally子句開始的位置。它收集所有catchfinally子句併爲它們建立適當的錯誤處理環境。

一旦它這樣做,它會正常執行所有的expr表格。所以它擴展它們的宏,然後執行它們。但catch不是一種功能或特殊形式,它只是try在前面的步驟中尋找的內容。所以當它正常執行時,你會得到與在REPL中鍵入相同的錯誤。

您應該做的應該是編寫一個宏,您可以圍繞您的整個代碼擴展到所需的try/catch表達式。沒有你想要完成的事例,很難給出具體的答案。

+0

我明白你在說什麼了,但是感覺問題是由於'catch'被評估得太早而不是太遲,這就是我如何閱讀你的答案,你不同意嗎?這也可以解釋爲什麼@arthur(我同意他不是最漂亮的解決方案)的答案有效:'catch'被拼接但尚未評估。我正在努力做到這一點,並繼續使用語法引用:-)我確實按照您的建議進行了操作,並將所有代碼都包含在一個宏中,要麼發佈該代碼,要麼發現該代碼是一個更加複雜的解決方案:-) – 0dB

+0

問題是,catch應該永遠不會被評估,沒有「太快」。 「catch」和「finally」並不是真正的操作符,即使它們是像它們一樣寫的。編譯器在掃描「try」主體時逐字地查找它們。 – Barmar

+0

沒錯,但我會認爲在宏觀處理過程中,這個嘗試首先被「粘貼」(引用),這可以解釋爲什麼@Arthur的解決方案能夠工作(即使不適合我)?無論如何,我發現令人困惑的是'macroexpand-all'顯示正在擴展,但它仍然無效。 – 0dB

1

簡短的回答是YES,雖然嵌套特殊形式的宏可能會導致一些像這樣的雙引號頭痛。這是一定要防止在擴大這兩個級別被評估的符號:

user> (defmacro catch-me []         
      '(list 'catch 'Exception 'ex 
         'true)) 

user> (defmacro try-me [] 
    `(try (+ 4 3)    
        ~(catch-me))) 
#'user/try-me 

user> (try-me) 
7 

,並看到它捕獲異常,以及:

user> (defmacro try-me [] 
    `(try (/ 4 0) 
       ~(catch-me))) 
#'user/try-me 
user> (try-me) 
true 
+0

由於你的建議和'(clojure.walk/macroexpand-all'(try-me))'都可以工作,我仍然試圖找到一個使用語法引用的工具。我會發布,如果我找到它或只是跟進@ Barmar的建議。 – 0dB