Use case #1: Ignore the exception unless a predicate returns true; return nil if an exception is ignored.
(defmacro filter-exception "Execute body of code and in case of an exception, ignore it if (pred ex) returns false (i.e. rethrow if true) and return nil." [pred & body] `(try ~@body (catch Exception e# (when (~pred e#) (throw e#)))))
This is useful for cases where one needs arbitrary control over how to determine whether an exception should be re-thrown or ignored. An example use case might be when re-throwing of exception is subject to external condition. Let's see it in action.
(def ^:dynamic *debug-mode* false) (filter-exception #(and *debug-mode* (instance? FooException %)) ...)
Use case #2: Specify which exceptions you want noticed and which ones should be ignored.
(defmacro with-exceptions "Execute body of code in the context of exceptions to be re-thrown or ignored. Args: throw-exceptions - List of exceptions that should be re-thrown leave-exceptions - List of exceptions that should be suppressed Note: 'throw-exceptions' is given preference over 'leave-exceptions' Example usage: ;; ignore all runtime exceptions except ;; IllegalArgumentException and IllegalStateException (with-exceptions [IllegalArgumentException IllegalStateException] [RuntimeException] ...)" [throw-exceptions leave-exceptions & body] `(filter-exception (fn [ex#] (cond (some #(instance? % ex#) ~throw-exceptions) true (some #(instance? % ex#) ~leave-exceptions) false :else true)) ~@body))
This macro is a convenience wrapper over filter-exception for the common scenario where one may like to specify which exceptions to re-throw and which ones to ignore. The usage is quite simple as the docstring says. The first vector of exceptions are the ones that should be re-thrown, and the second that should be ignored.
(with-exceptions [IllegalArgumentException IllegalStateException] [RuntimeException] "foo" ; non-effective return value (throw (IllegalArgumentException. "dummy")))
In the snippet above, it will re-throw the exception is because it is listed in the first vector.
(with-exceptions [IllegalArgumentException IllegalStateException] [RuntimeException] "foo" ; non-effective return value (throw (NullPointerException. "dummy")))
In this case the exception will be swallowed. Why? Because NullPointerException is a sub-class of RuntimeException and is hence an instance of RuntimeException too!
Hope you find this discussion useful. Feel free to post your comments. You may like to follow me on Twitter.