2012-10-29 4 views
6

Die try ist in einem Makro, die catch in einer zweiten, die von der ersten aufgerufen wird. Wie bekomme ich das Folgende zur Arbeit?Kann versuchen, in verschiedenen (aber verschachtelten) Makros zu sein?

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

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

try-me Erweiterung sieht gut aus:

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

ergibt

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

aber Aufruf (try-me) ergibt:

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

die, BTW, ist auch die Nachricht, die Sie in der REPL bekommen würden wenn Sie Fang verwenden, wenn Sie nicht versuchen.

UPDATE:

Dies ist, wie ich es (danke, @Barmar) zu arbeiten bekommen, hier können Sie den aktuellen Kontext meines Codes sehen:

(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]]))))) 

aber das ist, was ich Hoffnung auf (ein separates Makro catch-me verwenden):

(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."))) 

ich denke, das wäre einfacher zu schreiben/aufrechtzuerhalten.

Irgendwelche Ideen? Ich brauche Syntax-Quoting, weil ich Parameter übergebe, deshalb kann Arthurs Antwort leider nicht angewendet werden (oder kann es irgendwie?), Aber ich habe meinen aktuellen Kontext erst jetzt gepostet.

+0

Meine aktuelle Vermutung ist, dass (Catch-me) wird zuerst erweitert, bevor es klar ist, dass es sich in einem Versuch befindet. Ist es das? Wie man das bekämpft? – 0dB

+1

Makros werden nur an Stellen erweitert, an denen ein Ausdruck normal ausgewertet würde. Die 'catch'-Teilformulare sind keine Ausdrücke, die ausgewertet werden, sie sind Teil der Syntax von' try'. – Barmar

+0

Ich halte diese Frage derzeit noch für unbeantwortet. Es ist verblüffend, warum das Beispiel von Arthur funktioniert, aber eines, das Syntax-Quoting verwendet, und das Makroexpand-all zeigt eine funktionierende Makroerweiterung, aber ein direkter Aufruf an es schlägt mit einem Fehler fehl. Danke an Barmar für die Problemumgehung. Irgendwelche anderen Ideen, irgendjemand? – 0dB

Antwort

5

Der Grund erhalten Sie, dass Fehler ist, weil die Syntax für try ist:

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

Das bedeutet, dass es vor dem Fang eine beliebige Anzahl von ausdr Formen sein können und schließlich Klauseln. try scannt die expr s, bis es eine findet, die mit catch oder finally beginnt. Es tut dies vor erweitern alle Makros, da es nur versucht, herauszufinden, wo die Ausdrücke excrs und Catch/finally beginnen. Es sammelt alle catch und finally-Klauseln und richtet die entsprechende Fehlerbehandlungsumgebung für sie ein.

Sobald es dies tut, führt es normalerweise alle expr Formulare aus. Also erweitert es ihre Makros und führt sie dann aus. Aber catch ist keine Funktion oder spezielle Form, es ist nur etwas, das im vorherigen Schritt try sucht. Wenn es normal ausgeführt wird, erhalten Sie den gleichen Fehler wie beim Eingeben in die REPL.

Was Sie wahrscheinlich tun sollten, ist ein Makro zu schreiben, das Sie um Ihren gesamten Code wrape, der in den gewünschten Ausdruck erweitert, try/catch. Ohne ein Beispiel dafür, was Sie erreichen möchten, ist es schwierig, eine konkrete Antwort zu geben.

+0

Ich sehe, was du sagst, aber hast das Gefühl, dass das Problem entsteht, weil der "Fang" zu früh und nicht zu spät bewertet wird, so lese ich deine Antwort, würdest du nicht zustimmen? Das würde auch erklären, warum die Antwort von @arthur (ich stimme ihm zu, dass es nicht die schönste Lösung ist) funktioniert: der 'catch' ist eingespleisst, aber noch nicht ausgewertet. Ich arbeite daran, das zu tun und benutze immer noch Syntax-Quoting :-) Ich habe deinen Vorschlag verfolgt und habe meinen gesamten Code in ein Makro geschrieben, werde das entweder posten oder, wenn ich es finde, eine nobelere Lösung :-) – 0dB

+0

Das Problem ist, dass "catch" niemals überhaupt ausgewertet werden sollte, es gibt kein "zu früh". 'catch' und' finally' sind keine echten Operatoren, auch wenn sie so geschrieben sind wie sie sind. Der Compiler sucht buchstäblich nach ihnen, wenn sie den Körper von "try" scannt. – Barmar

+0

Richtig, aber ich würde denken, dass während der Makroverarbeitung der Versuch zuerst einfach "eingefügt" (zitiert) wird, was erklären würde, warum @ Arthurs Lösung funktioniert (wenn auch nicht für mich)? In jedem Fall finde ich es verwirrend, dass "macroexpand-all" eine funktionierende Erweiterung zeigt, aber trotzdem nicht funktioniert. – 0dB

1

Die kurze Antwort ist JA, obwohl das Verschachteln von Makros mit speziellen Formen zu einigen doppelten Zitaten führen kann.Es ist unbedingt die Symbole, um zu verhindern auf beiden Ebenen der Expansion ausgewertet:

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 

und zu sehen, dass es die Ausnahme abfängt auch:

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

Da Ihr Vorschlag und '(clojure.walk/macroexpand-all '(try-me))' beide funktionieren, versuche ich immer noch einen zu finden, der Syntax-Zitate verwendet. Ich poste wenn ich es finde oder folge einfach @ Barmars Vorschlag. – 0dB

Verwandte Themen