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

Reply via email to