Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can try and catch be in different (but nested) macros?

The try is in one macro, the catch in a second one that is called by the first. How to get the following to work?

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

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

Expanding try-me looks good:

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

yields

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

but calling (try-me) yields:

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

which, BTW, is also the message you would get in the REPL when using catch when not in a try.

UPDATE:

This is how I can get it to work (thanks, @Barmar), here you can see the actual context of my code:

(defmacro try-me [& body]
  `(try
     ~@body
     ~@(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]])))))

but this is what I was hoping for (using a separate macro catch-me):

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

I think this would be easier to write / maintain.

Any ideas? I need syntax-quoting because I am passing parameters, that is why unfortunately Arthur's answer cannot be applied (or can it somehow?), but I didn't post my actual context until just now.

like image 669
0dB Avatar asked Oct 29 '12 21:10

0dB


People also ask

Is nested try-catch bad practice?

Although this is sometimes unavailable, nesting try/catch blocks severely impacts the readability of the source code as it makes it difficult to understand which block will catch which exception.

Is it OK to have try-catch inside try-catch?

Yes, we can declare a try-catch block within another try-catch block, this is called nested try-catch block.

Can you nest try-catch blocks JavaScript?

You can nest one or more try statements. If an inner try statement does not have a catch -block, the enclosing try statement's catch -block is used instead. You can also use the try statement to handle JavaScript exceptions. See the JavaScript Guide for more information on JavaScript exceptions.

Can we use try-catch inside catch C#?

If no catch block is found, then the CLR displays an unhandled exception message to the user and stops execution of the program. It is possible to use more than one specific catch clause in the same try-catch statement.


2 Answers

The reason you get that error is because the syntax for try is:

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

This means that there can be any number of expr forms before the catch and finally clauses. try scans the exprs until it finds one that begins with catch or finally. It does this before expanding any macros, since it's just trying to figure out where the exprs and and the catch/finally clauses begin. It collects all the catch and finally clauses and establishes the appropriate error handling environment for them.

Once it does this, it executes all the expr forms normally. So it expands their macros, and then executes them. But catch is not a function or special form, it's just something that try looks for in the earlier step. So when it's executed normally, you get the same error as when you type it into the REPL.

What you should probably do is write a macro that you wrape around your entire code that expands into the try/catch expression that you want. Without an example of what you're trying to accomplish, it's hard to give a specific answer.

like image 60
Barmar Avatar answered Nov 13 '22 23:11

Barmar


The short answer is YES, though nesting macros with special forms can lead to some double-quoting headaches like this one. It is necessarily to prevent the symbols from being evaluated at both levels of expansion:

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

and to see that it catches the exception as well:

user> (defmacro try-me []
    `(try (/ 4 0)
              ~(catch-me)))
#'user/try-me
user> (try-me)
true
like image 43
Arthur Ulfeldt Avatar answered Nov 13 '22 23:11

Arthur Ulfeldt