Creating exception class

213 views
Skip to first unread message

jim

unread,
Apr 17, 2008, 11:33:04 PM4/17/08
to Clojure
Hey,

I'd like to create custom exception/error classes that can be thrown
and caught in Clojure. Not being a Java guy, I'm hoping someone can
point me to where I can learn how to do that.

Thanks,
Jim

MikeM

unread,
Apr 18, 2008, 12:06:55 PM4/18/08
to Clojure
I'm not sure if this is what you're looking for, just a very simple
example.

(defn myEx [#^String message]
(proxy [Exception] [message]))

(try
(prn "in try")
(throw (myEx "my exception"))
(catch Exception e (prn "got exception:" e)) (finally (prn "in
finally")))

jim

unread,
Apr 18, 2008, 12:45:18 PM4/18/08
to Clojure
Almost, except that the catch will catch any exception, not just the
custom one.

jim

Stuart Sierra

unread,
Apr 18, 2008, 3:04:00 PM4/18/08
to Clojure
On Apr 17, 11:33 pm, jim <jim.d...@gmail.com> wrote:
Well, the normal Java way to define a new exception is to sub-class
java.lang.Exception (or the more generic java.lang.Throwable). You
can sort of do that with a proxy in Clojure, as MikeM showed, but it
won't be a class that can be caught by name.

I think the only way to achieve this in pure Clojure is to define
additional fields/methods on your proxy. Then do something like this:

(try
(code-that-may-throw-custom-exception)
(catch Exception e
(when-not (. e (myExceptionFlag)) (throw e))
(handle-my-exception)))

-Stuart

jon

unread,
Apr 18, 2008, 3:16:56 PM4/18/08
to Clojure
Here's my technique for addressing this issue.. which I think could be
a worthwhile addition to core clojure since it's in keeping with "the
clojure way".

I created a generic CljExeption which extends Exception and implements
IObj so that it supports metadata. (Code attached below, which I
compile against clojure.jar)

I use this function which has quite a clean syntax to create new
exceptions, specifying the 'message' and 'cause' along with any other
necessary
data in the metadata.
--------------------
(defn ex-clj
([]
(clojure.lang.CljException.))
([meta]
(if (contains? meta :mesg)
(with-meta (clojure.lang.CljException. (:mesg meta) (:cause
meta)) meta)
(with-meta (clojure.lang.CljException. (:cause meta)) meta))))
------
e.g.
(throw (ex-clj))

(throw (ex-clj {:mesg "error 123", :userfriendly "a bad thing
happened"}))

(catch BlahException e
(throw (ex-clj {:mesg "error 123", :cause e, :extra
"info", :tobelogged true})))
----------------------

With this,
- You don't have to derive any further exception classes in java.
- They can be caught without having to catch everything derived from
'Exception'.
- You can easily get at the metadata after catching.
- Because the 'message' and 'cause' is also duplicated in the metadata
it's easy to access that without using java-interop syntax.
- Because it's completely general, it then becomes possible to write a
new 'try' macro which will insert code to automatically catch any
CljExceptions, and then (a bit like the multimethod concept) select
the relevant 'catch' clause by evaluating test-expressions (ie.like a
case-statement) utilizing any part of the metadata, rather than solely
matching on the class of the exception which is less powerful. If no
matches are found, the CljException can then be automatically re-
thrown.

What does anyone think?
Thanks,
Jon

------------ clojure/lang/CljException.java ----------------
package clojure.lang;

public class CljException extends Exception implements IObj {
final IPersistentMap _meta;

public CljException() {
super();
_meta = null;
}

public CljException(Throwable cause) {
super(cause);
_meta = null;
}

public CljException(String message) {
super(message);
_meta = null;
}

public CljException(String message, Throwable cause) {
super(message, cause);
_meta = null;
}

private CljException(IPersistentMap meta, String message) {
super(message);
_meta = meta;
}

private CljException(IPersistentMap meta, String message,
Throwable cause) {
super(message, cause);
_meta = meta;
}

final public IPersistentMap meta() {
return _meta;
}

final public CljException withMeta(IPersistentMap meta) {
if (meta != _meta)
return new CljException(meta, getMessage(), getCause());
return this;
}
}
---------------------------------------

MikeM

unread,
Apr 19, 2008, 10:00:29 AM4/19/08
to Clojure


On Apr 18, 3:16 pm, jon <superuser...@googlemail.com> wrote:
> Here's my technique for addressing this issue.. which I think could be
> a worthwhile addition to core clojure since it's in keeping with "the
> clojure way".
>
> I created a generic CljExeption which extends Exception and implements
> IObj so that it supports metadata. (Code attached below, which I
> compile against clojure.jar)
>

This looks like a nice use for meta data.

I had another thought on the issue raised by jim - how to create a
java subclass in clojure. Proxy lets us do that, but it doesn't allow
us to name the class. Reading proxy.clj, it seems that proxy could
support optional user-naming of proxy classes. In proxy.clj :

(defn get-proxy-class
...
cname (str "clojure/lang/" (gensym "Proxy__"))

The optional name would be used in place of the gensym'ed
clojure.lang.Proxy...

This would be useful for jim's case, where the naming of the proxy
class would allow straightforward use of try..catch. There are
probably other cases where this would be useful.

Issues to address - attempts to re-use a class name, what to do if the
proxy class already exists with a name different than that specified
by the optional classname arg. I guess this is a question for Rich or
perhaps someone else with a deeper understanding of how proxy works.

Rich Hickey

unread,
Apr 20, 2008, 5:27:56 PM4/20/08
to Clojure


On Apr 18, 3:04 pm, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> On Apr 17, 11:33 pm, jim <jim.d...@gmail.com> wrote:
>
> > I'd like to create custom exception/error classes that can be thrown
> > and caught in Clojure. Not being a Java guy, I'm hoping someone can
> > point me to where I can learn how to do that.
>
> Well, the normal Java way to define a new exception is to sub-class
> java.lang.Exception (or the more generic java.lang.Throwable). You
> can sort of do that with a proxy in Clojure, as MikeM showed, but it
> won't be a class that can be caught by name.
>

Right. For now, you can't name your proxies, but I am looking into
putting in the infrastructure for some naming support.

> I think the only way to achieve this in pure Clojure is to define
> additional fields/methods on your proxy. Then do something like this:
>
> (try
> (code-that-may-throw-custom-exception)
> (catch Exception e
> (when-not (. e (myExceptionFlag)) (throw e))
> (handle-my-exception)))
>

That won't work either - you can't add fields/methods to a proxy.

Rich

jon

unread,
Apr 21, 2008, 8:46:46 AM4/21/08
to Clojure
Hi Rich,
I'd be interested to hear whether you think the idea of a single,
generically re-usable "ClojureException" class is a good avenue to
pursue or whether you have more comprehensive plans for succinct
clojure idioms to handle exceptions?

Being able to simply attach regular clojure data at throw-time (housed
in a map seems reasonable) and have that easily examinable when caught
gets things out of the 'class hierarchy' game straight into the
'standard clojure data-structure' game with all the facilities and
standard practices that brings. It seems to marry up with the usual
clojure way of doing things, but shouldn't upset JVM/Java interop
('message' and 'cause' are as normal, etc).

Achieving this using metadata might not be 'the right thing' if
strictly speaking the attached data is not 'meta'.. it's just the
quick and obvious way because '^' and (with-meta) already come for
free. The exception could easily have both the 'payload' data _and_
metadata to keep coding options even more open.
Thanks, Jon
Reply all
Reply to author
Forward
0 new messages