2012-10-29 16 views
6

try è in una macro, la catch in una seconda chiamata dal primo. Come ottenere il seguente lavoro?È possibile provare a catturare in macro diverse (ma nidificate)?

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

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

Espansione try-me sembra buono:

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

cede

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

ma chiamando il numero (try-me) i rendimenti:

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

che, BTW, è anche il messaggio che si otterrebbe nella REPL quando si usa la presa quando non ci si prova.

UPDATE:

Questo è come posso farlo funzionare (grazie, @Barmar), qui potete vedere il contesto attuale del mio codice:

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

ma questo è ciò che ero sperando in (utilizzando una macro separata 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."))) 

penso che questo sarebbe stato più facile da scrivere/mantenere.

Qualche idea? Ho bisogno di sintassi-quoting perché sto passando dei parametri, è per questo che purtroppo la risposta di Arthur non può essere applicata (o può in qualche modo?), Ma non ho postato il mio contesto attuale fino ad ora.

+0

Il mio attuale ipotesi è che (catch-me) viene prima espanso, prima che sia chiaro che è all'interno di una prova. È così? Come combatterlo? – 0dB

+1

I macro vengono espansi solo nei punti in cui un'espressione viene valutata normalmente. Le sottomasche 'catch' non sono espressioni che vengono valutate, fanno parte della sintassi di' try'. – Barmar

+0

Attualmente ritengo ancora questa domanda come senza risposta. È sconcertante il motivo per cui l'esempio di Arthur funziona, ma uno che usa la sintassi della sintassi non lo fa, e, che macroexpand-all mostra un'espansione della macro funzionante, ma una chiamata diretta ad essa fallisce con un errore. Grazie a Barmar per la soluzione. Qualche altra idea, qualcuno? – 0dB

risposta

5

La ragione che si ottiene questo errore è perché la sintassi per try è:

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

Questo significa che non ci può essere un numero qualsiasi di espr forme prima della cattura e infine clausole. try esegue la scansione di expr s finché non ne trova uno che inizi con catch o finally. Lo fa prima di espandendo qualsiasi macro, dal momento che sta solo cercando di capire dove iniziano le clausole exprs e catch/finally. Raccoglie tutte le clausole catch e finally e stabilisce per esse l'ambiente di gestione degli errori appropriato.

Una volta eseguita, esegue tutti i moduli expr normalmente. Quindi espande i loro macro e quindi li esegue. Ma catch non è una funzione o un modulo speciale, è solo qualcosa che lo try cerca nel passaggio precedente. Quindi, quando viene eseguito normalmente, si ottiene lo stesso errore di quando lo si digita nel REPL.

Quello che dovresti fare è scrivere una macro che avvolga tutto il tuo codice che si espande nell'espressione try/catch che desideri. Senza un esempio di ciò che stai cercando di realizzare, è difficile dare una risposta specifica.

+0

Vedo quello che stai dicendo, ma ho la sensazione che il problema derivi dal fatto che il "pescato" viene valutato troppo presto e non troppo tardi, il che è il modo in cui ho letto la tua risposta, non sei d'accordo? Ciò spiegherebbe anche perché la risposta di @arthur (sono d'accordo con lui sul fatto che non è la soluzione più carina) funziona: il 'catch' è unito ma non ancora valutato. Sto lavorando per ottenere ciò che sta accadendo e sto ancora usando la sintassi-citazione :-) Ho seguito il tuo suggerimento e ho avvolto tutto il mio codice in una macro, o lo pubblicheremo o, se lo trovo, una soluzione niftier :-) – 0dB

+0

Il problema è che 'catch' non dovrebbe mai essere valutato, non c'è "troppo presto". 'catch' e' finally' non sono veri operatori, anche se sono scritti come sono. Il compilatore li cerca letteralmente durante la scansione del corpo di 'try'. – Barmar

+0

Giusto, ma penserei che durante l'elaborazione delle macro, all'inizio il tentativo sia solo 'incollato' (quotato), il che spiegherebbe perché la soluzione di @ Arthur funziona (anche se non per me)? In ogni caso, trovo confuso il fatto che 'macroexpand-all' mostri un'espansione funzionante, ma che comunque non funzioni. – 0dB

1

La risposta breve è SÌ, anche se le macro di annidamento con moduli speciali possono portare ad alcuni mal di testa a virgolette doppie come questa.E 'necessariamente per evitare che i simboli in corso di valutazione, sia a livello di espansione:

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 

e di vedere che cattura l'eccezione così:

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

Dato che il tuo suggerimento e '(clojure.walk/macroexpand-all '(try-me))' funzionano entrambi sto ancora cercando di trovarne uno che usi la sintassi-quoting. Pubblicherò se lo trovo o seguirò il suggerimento di @ Barmar. – 0dB

Problemi correlati