Weird exception behavior

3 views
Skip to first unread message

jim

unread,
Oct 23, 2008, 10:11:05 PM10/23/08
to Clojure
Rich,

When I do the following:

(gen-and-load-class 'user.UserException :extends Exception)

(defn th [arg]
(throw (new user.UserException "thrown exception")))

(defn test-fn []
(try
(dorun (map th '(1 2 3)))
(catch user.UserException e
(println "caught" e))
(finally (println "finally clause"))))

and then execute test-fn, the exception is not caught.

user=> (test-fn)
finally clause
java.lang.RuntimeException: user.UserException: thrown exception
(NO_SOURCE_FILE:0)

So am I doing something wrong or is this a bug when an exception is
thrown from inside a call to map?

Thanks,
Jim

Rich Hickey

unread,
Oct 24, 2008, 7:39:55 AM10/24/08
to Clojure
Currently ISeq (the interface for seqs) doesn't declare any exceptions
on first/rest, therefor no implementations, e.g. LazyCons, can throw
checked exceptions. So, if they encounter an exception in the
implementation closure, they have to wrap it in an unchecked
exception, like RuntimeException.

This is a good example of why checked exceptions are bad - they force
intervening parties who should have no participation in the process to
be involved in exception declarations and handling, thereby thwarting
the basic premise of exceptions - communication between the party that
had the problem and the party that can do something about it.

In any case, the only way to fix this is to declare that first/rest
throw Exception in ISeq, which would be a big change (because
everything that calls them would then have to declare exceptions, and
everything that calls them... - complete inanity).

They definitely weren't thinking about closures and higher-order
programming when they invented checked exceptions - it invariably
leads to everything declaring they throw the root Exception, so why
bother?

Rich

Paul Stadig

unread,
Oct 24, 2008, 7:46:45 AM10/24/08
to clo...@googlegroups.com
So the moral of the story (in terms of idiomatic clojure) is to
declare RuntimeExceptions instead of Exceptions (if you need to
declare an exception)? Or is there a better way to handle errors?

Paul

Rich Hickey

unread,
Oct 24, 2008, 8:18:16 AM10/24/08
to Clojure


On Oct 24, 7:46 am, "Paul Stadig" <p...@stadig.name> wrote:
> So the moral of the story (in terms of idiomatic clojure) is to
> declare RuntimeExceptions instead of Exceptions (if you need to
> declare an exception)? Or is there a better way to handle errors?
>

You don't declare exceptions in Clojure. If you mean derive from
RuntimeException, that won't help, as at present most wrapping
contexts just catch any Exception (which will catch RuntimeExceptions)
and throw wrapped in a new RuntimeException. I recommend you try to
use an existing Exception type when possible (there are too many
already), and derive from Exception otherwise.

The real issue is on the handling side. In general, I think unless you
are directly calling something that declares specific exceptions, you
need to catch Exception then see what you've got, including examining
the cause. You should presume that all generic plumbing, i.e. most of
Clojure, might throw Exceptions, sometimes wrapping others.

Rich

jim

unread,
Oct 24, 2008, 9:46:30 AM10/24/08
to Clojure
Thanks Rich.

Paul Stadig

unread,
Oct 24, 2008, 9:47:12 AM10/24/08
to clo...@googlegroups.com
Right, I meant derive. Sorry. I was just trying to understand the best
way to deal with errors in Clojure. I thought what you were saying was
that we could derive exceptions from RuntimeException and they
wouldn't get wrapped. It seems to negate the value of try/catch if
every exception (including RuntimeExceptions) are wrapped in
RuntimeExceptions that have to be unwrapped to find the true cause. I
have noticed in the REPL in emacs I have to usually drill down three
or four levels to find the true cause (and file and line no) of an
exception.

However, since I'm relatively new here, I did a search in the archive,
and turned up this message, which seems to layout a better, more
Clojureish way to handle errors using dynamic vars bound to 'recovery'
functions.

http://groups.google.com/group/clojure/msg/842e8bb058015282


Paul

Christopher Taylor

unread,
Oct 25, 2008, 7:59:35 AM10/25/08
to clo...@googlegroups.com, Rich Hickey
On 24.10.2008 13:39 Uhr, Rich Hickey wrote:
>> user=> (test-fn)
>> finally clause
>> java.lang.RuntimeException: user.UserException: thrown exception
>> (NO_SOURCE_FILE:0)
>>
>> So am I doing something wrong or is this a bug when an exception is
>> thrown from inside a call to map?
>>
>
> Currently ISeq (the interface for seqs) doesn't declare any exceptions
> on first/rest, therefor no implementations, e.g. LazyCons, can throw
> checked exceptions. So, if they encounter an exception in the
> implementation closure, they have to wrap it in an unchecked
> exception, like RuntimeException.
>
> This is a good example of why checked exceptions are bad - they force
> intervening parties who should have no participation in the process to
> be involved in exception declarations and handling, thereby thwarting
> the basic premise of exceptions - communication between the party that
> had the problem and the party that can do something about it.
>
> In any case, the only way to fix this is to declare that first/rest
> throw Exception in ISeq, which would be a big change (because
> everything that calls them would then have to declare exceptions, and
> everything that calls them... - complete inanity).

coming from a Java perspective, what's wrong with the clojure plumbing
catching all Exceptions and wrapping them in a (checked)
ClojureException - maybe derived from Throwable, so it wouldn't
interfere with Java's exception hierarchy - which (catch) would then
unwrap. Of course this would imply most of Clojure's Java implementation
declaring "throws ClojureException", but this would unify the exception
handling "interface" towards code written in clojure.

--Chris

PS: Rich, I'm CC'ing you, because I've never been able to post to the list.

Reply all
Reply to author
Forward
0 new messages