Have you signed a CA yet? If so, you can sign up for a confluence account and start a wiki page around this. This way the appropriate discussions and review can take place and then possibly a JIRA ticket/patch to fix things up.
Cheers, Aaron Bedra -- Clojure/core http://clojure.com On Sat, May 21, 2011 at 12:42 AM, Ken Wesson <kwess...@gmail.com> wrote: > => (try (doall (map (partial / 36) [4 9 3 2 0])) (catch > ArithmeticException _ "boo!")) > #<CompilerException java.lang.RuntimeException: > java.lang.ArithmeticException: Divide by zero (NO_SOURCE_FILE:0)> > > The problem with trying to catch exceptions thrown from inside lazy > sequence generators is that they all get wrapped in RuntimeException. > > Catching that works: > > => (try (doall (map (partial / 36) [4 9 3 2 0])) (catch > RuntimeException _ "boo!")) > "boo!" > > but doesn't allow catch-dispatch on exception type. > > You can get the final cause of an exception with this: > > (defn get-cause [t] > (loop [t t] > (if-let [c (.getCause t)] > (recur c) > t))) > > You could make a catch-last macro. > > (defmacro catch-last [& catches] > `(catch Throwable t# > (let [cause# (get-cause t#)] > (try (throw cause#) > ~@catches)))) > > seems like it should work, and macroexpands properly, but fails oddly: > > => (try (doall (map (partial / 36) [4 9 3 2 0])) (catch-last > ArithmeticException _ "boo!")) > #<CompilerException java.lang.Exception: Unable to resolve symbol: > catch in this context (NO_SOURCE_FILE:1)> > > If you ask me, that's a compiler bug right there; it should expand to > (try (doall ...) (catch (let ...))) which is legal Clojure and a legal > try-catch form in particular. > > This, instead, works: > > (defmacro try-2 [& body] > (let [[try-part catches] (split-with #(not= (first %) 'catch) body) > [catches finally] (split-with #(not= (first %) 'finally) catches)] > `(try > ~@try-part > (catch Throwable t# > (let [cause# (get-cause t#)] > (try (throw cause#) > ~@catches))) > ~@finally))) > > => (try-2 (doall (map (partial / 36) [4 9 3 2 0])) (catch > ArithmeticException _ "boo!")) > "boo!" > > However, if you call a Java API that wraps exceptions, you might want > to catch the Java layer exception and not its own cause. That is, you > could have > > Exception in thread "Thread-1989" java.lang.RuntimeException: blah blah > at <stack trace here> > Caused by com.foo.FooFrameworkLoadResourceException: blah blah > at <stack trace here> > Caused by org.xml.sax.SAXException: blah blah > at <stack trace here> > > and you probably don't care that the framework encountered some > malformed XML on the web, but that it failed to load a resource. > Ordinary try will catch the RuntimeExcepion and try-2 will catch the > SAXException; neither will catch the > FooFrameworkLoadResourceException. > > The simplest thing Clojure could do to alleviate this type of > difficulty is to not wrap in generic RuntimeExceptions. Instead, > derive ClojureRuntimeException from it and use that, and we can > redefine get-cause as follows: > > (defn get-cause [t] > (loop [t t] > (if (instance? ClojureRuntimeException t) > (recur (.getCause t)) > t))) > > and then try-2 will behave as desired for cases like this. > > But then there's so much more that could be done, such as allowing > error handlers to be called as soon as various conditions arise that > can perform restarts. The options would generally be to a) propagate > the exception, b) retry, c) throw an Error, and d) return some other > value as the value of the expression whose evaluation aborted. So if > we set up to propagate, the behavior would be the same as now. On the > other hand if we set up to restart, we might use a handler like: > (syntax speculative) > > (handler ArithmeticException _ Double/NaN) > > in case we'd rather have NaNs propagate through failed math, for example, > or > > (handler IOException _ nil) > > if we want some I/O read operation to return nil on failure. How about > > (handler OutOfMemoryError _ (reset! cache {}) (retry)) > > with the proviso that retry reruns the failed expression as if retry > were a zero-argument function containing it as its body and any > unhandled exceptions thrown out of a handler are propagated. This > allows propagation either via (handler FooException e (throw e)) or by > not having a handler registered for FooException. Multiple retries > would require try...catch inside the handler. > > The default in REPL sessions could even be to pop up a message box via > Swing and prompt the user what to do, not unlike the errors one gets > developing in typical Common Lisp environments: > > > > ! Error x _ # > > FooException in foo-function > > (x) Propagate the exception > ( ) Retry > ( ) Throw Error > ( ) Evaluate to this value: [nil ] > > (Copy stack trace to clipboard) (OK) > > > > where the field for the third option is of course passed through > read-string or maybe even eval and "throw Error" would be omitted if > the exception was already an Error. > > The proposed handler syntax deliberately looks like existing catch clauses. > > The hairy details include how one would set handlers -- updating a > thread-local map of exception types to handler closures? The default > for any given exception class would be propagate, of course. Another > hairy detail would be how to resolve conflicts -- also, when to invoke > handlers. Given the need for a handler to capture the failed > evaluation, it might require something like wrapping function bodies > in a try ... catch that punts to the handlers or rethrows as needed. > Lazy sequence arguments' heads will get held until the function > returns, which may be very bad. It might be hard to make this work and > have acceptable performance if it's dynamic. > > For now, though, at the very least try-2 and the notion of a > ClojureRuntimeException wrapper class in future versions of Clojure > are viable improvements. > > (Interestingly, the try-2 macro would cause the catch-last behavior > observed if it weren't already happening and you used catch-last > inside try-2. This indicates that try is being expanded like a macro > instead of processed after macroexpansion. Other special forms exhibit > similar behavior, e.g. you can't have def's first argument come out of > a macro expansion unless the whole def form does, but it's unexpected > in the case of try because (catch ...) and (finally ...) "feel like" > special forms in their own right, which certainly should not work > outside of a try but seem like they should be separately generatable > by macros the way other forms in the try body can be.) > > -- > Protege: What is this seething mass of parentheses?! > Master: Your father's Lisp REPL. This is the language of a true > hacker. Not as clumsy or random as C++; a language for a more > civilized age. > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en