Re: We need a better way to handle errors.
On May 23, 12:24 am, Aaron Bedra aaron.be...@gmail.com wrote: 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. Discussions about improving Clojure error handling are already under way: http://dev.clojure.org/display/design/Error+Handling The RuntimeException wrapping problem seems not to have been brought up yet, though. -- 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
Re: We need a better way to handle errors.
On Mon, May 23, 2011 at 1:28 PM, Daniel Werner daniel.d.wer...@googlemail.com wrote: On May 23, 12:24 am, Aaron Bedra aaron.be...@gmail.com wrote: 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. Discussions about improving Clojure error handling are already under way: http://dev.clojure.org/display/design/Error+Handling Hmm. The RuntimeException wrapping problem seems not to have been brought up yet, though. It would be simple, backward compatible with code that catches (or lets slip) RuntimeException, and a substantial improvement to create a ClojureRuntimeException subclass of RuntimeException and add a function to core that takes an exception and, if it's a ClojureRuntimeException instance, recurses down the .getCause() chain until it isn't and then returns it. This would return the outermost exception generated by an API and not wrapped by Clojure HOFs. -- 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
Re: We need a better way to handle errors.
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
We need a better way to handle errors.
= (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