Re: We need a better way to handle errors.

2011-05-23 Thread Daniel Werner
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.

2011-05-23 Thread Ken Wesson
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.

2011-05-22 Thread Aaron Bedra
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.

2011-05-20 Thread Ken Wesson
= (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