throwing the underlying exception

571 views
Skip to first unread message

Stuart Halloway

unread,
Oct 11, 2011, 8:13:01 PM10/11/11
to cloju...@googlegroups.com
Re: the reflector-wrapping-exception problem (http://groups.google.com/group/clojure/msg/68a7a47884ff119e).

What is the easiest way to get the Clojure Reflector to throw the underlying exception, even though it is not declared to do so?

Oh, and is this a good idea?

Stu

Herwig Hochleitner

unread,
Oct 12, 2011, 6:54:59 AM10/12/11
to cloju...@googlegroups.com
2011/10/12 Stuart Halloway <stuart....@gmail.com>:

> What is the easiest way to get the Clojure Reflector to throw the underlying exception, even though it is not declared to do so?

This method might come in handy here:
http://james-iry.blogspot.com/2010/08/on-removing-java-checked-exceptions-by.html

> Oh, and is this a good idea?

I'd say it's good as long as you stay in clojure.
When calling from java you'll have to deal with undeclared checked
exceptions. You can still catch those (as detailed in the article),
but undeclared checked exceptions are arguably worse that declared
checked exceptions ;)

--
__________________________________________________________________
Herwig Hochleitner

Stuart Sierra

unread,
Oct 12, 2011, 8:52:23 AM10/12/11
to cloju...@googlegroups.com
And interesting idea from the main Clojure list:
https://groups.google.com/d/msg/clojure/I5l1YHVMgkI/_7PzxDay9Q4J

Instead of throwing RuntimeException, throw a special Clojure subclass of RuntimeException. try/catch can always unwrap the special subclass, but not other wrapped exceptions.

-S

Sean Corfield

unread,
Oct 12, 2011, 1:27:17 PM10/12/11
to cloju...@googlegroups.com

Yup, if the unwrapping is automatic, I'd be in favor of using a
ClojureRuntimeException instead of the current generic
RuntimeException wrapper.
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Kevin Downey

unread,
Oct 12, 2011, 1:31:24 PM10/12/11
to cloju...@googlegroups.com
if (try …) becomes a macro over (try* …) it can expand in the
unwrapping for minimal compiler changes

> --
> You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
> To post to this group, send email to cloju...@googlegroups.com.
> To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.
>
>

--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

pmbauer

unread,
Oct 12, 2011, 6:46:24 PM10/12/11
to cloju...@googlegroups.com
fyi: http://dev.clojure.org/jira/browse/CLJ-855
There may be a better way than wrapping/unwrapping.  Thoughts?

Herwig Hochleitner

unread,
Oct 12, 2011, 10:27:24 PM10/12/11
to cloju...@googlegroups.com
2011/10/13 pmbauer <paul.mich...@gmail.com>:

> fyi: http://dev.clojure.org/jira/browse/CLJ-855
> There may be a better way than wrapping/unwrapping.  Thoughts?

I suppose, you are referring to "chucked" exceptions (thanks, James
Iry for coining that term :-)

When I laid out my argument against throwing chucked exceptions,
because of it making the life of interop users harder, I realized that
you can get a checked exception from a IFn.invoke() anyways. After all
you can always do a (throw (Exception.)) in clj code anyway. Not to
mention non-reflective interop calls from clojure.

So IMO that makes the case for "chucking" exceptions in Reflector.java
pretty solid.

It leaves the question how to catch chucked exceptions from java.
Following the approach of James Iry, I propose having

public class Util {
public static <T extends Throwable> void declareThrows(Class<T> c) throws T {}
public static <T1 extends Throwable, T2 extends Throwable> void
declareThrows(Class<T1> c1, Class<T2> c2) throws T1, T2 {}
// ... ad nauseum ...
// ... also as a convenience ...
public static <T extends Throwable> Object invokeThrowing(Class<T>
c, IFn fn, Object... args) throws T { return fn.invoke(args); }
}

so we can do

try {
Util.declare(IOException.class);
return slurp.invoke(io_file.invoke("~", ".soup"));
} catch (IOException e) { return "no soup"; }

or in this common case

try {
return Util.invokeThrowing(IOException.class, slurp,
io_file.invoke("~", ".soup"));
} catch (IOException e) { return "no soup"; }

I have taken the freedom to implement this approach and attach a patch
to http://dev.clojure.org/jira/browse/CLJ-855

kind regards
--
__________________________________________________________________
Herwig Hochleitner

Constantine Vetoshev

unread,
Dec 12, 2011, 1:13:18 PM12/12/11
to cloju...@googlegroups.com
I'd like to bring up http://dev.clojure.org/jira/browse/CLJ-855 again
— this is about exceptions becoming universally wrapped in
RuntimeExceptions when passing through clojure.lang.Reflector. The
JIRA ticket now has several proposed patches, implementing two
different solution strategies. Perhaps someone in core could look at
these patches? It would be great to have this problem solved in the
next release.

Best,
Constantine Vetoshev

Kevin Downey

unread,
Dec 12, 2011, 4:03:34 PM12/12/11
to cloju...@googlegroups.com

this is a real problem for interop, both ways.

having multiple patches is a drag, hard to tell which one to go with.
it seems like the sneaky throw patch is the one to use? dunno

Constantine Vetoshev

unread,
Dec 13, 2011, 12:43:28 AM12/13/11
to cloju...@googlegroups.com
On Dec 12, 2011, at 13:03, Kevin Downey wrote:
> On Mon, Dec 12, 2011 at 10:13 AM, Constantine Vetoshev
> <gepa...@gmail.com> wrote:
>> I'd like to bring up http://dev.clojure.org/jira/browse/CLJ-855 again
>
> having multiple patches is a drag, hard to tell which one to go with.
> it seems like the sneaky throw patch is the one to use? dunno

Other things being equal, I think that the patch with better
performance should go in. Perhaps someone with a better understanding
of the JVM speed tradeoffs of the two approaches could chime in?

Best,
Constantine Vetoshev

Ben Smith-Mannschott

unread,
Dec 13, 2011, 4:44:44 AM12/13/11
to cloju...@googlegroups.com

I've provided two different solutions to the patches because they
represent different trade-offs. I don't have a strong opinion as to
which would be preferable.

(1) The 'unwraps' patch hews closest to the current implementation,
specifically the 8fda34e4c77 change which chose to wrap checked
exceptions in RuntimeExceptions as close to the source as possible so
as not to need to declare 'throws Exception' all over the place.

The essence of the 'unwraps' patch is just to teach Clojure's try form
to automatically recognize and unwrap such exceptions before passing
them on to the catch clauses.

(2) The 'sneaky throws' patch undoes the 8fda34e4c77 decision to wrap
all checked exceptions, but still manages to be rid of the 'throws
Exception' problem.

It seems clear to me that 'sneaky throws' will have better performance
since it doesn't incur the wrap/unwrap overhead nor does it incur the
overhead of a more complex try form. (Does this even matter? We are,
after all, talking about *exception* handling.)

I consider 'unwraps' less risky since it layers on top of what is
already there, which I assume to be solid.

I'd prefer a solution like 'sneaky throws', but I think it requires
someone who remembers what the big picture was when they wrote
8fda34e4c77. (You'll see the JIRA issue mentions some questions.)

8fda34e4c77, for the curious:

https://github.com/clojure/clojure/commit/8fda34e4c77cac079b711da59d5fe49b74605553

// Ben


> Best,
> Constantine Vetoshev

Constantine Vetoshev

unread,
Jan 12, 2012, 5:12:33 PM1/12/12
to cloju...@googlegroups.com
On Mon, Dec 12, 2011 at 10:13 AM, Constantine Vetoshev
<gepa...@gmail.com> wrote:

Just sending another reminder about CLJ-855. It has been an
outstanding problem since 1.3 came out, and a patch has been available
for three months now. Judging by activity on the original thread
(http://groups.google.com/group/clojure/browse_thread/thread/23997560754c8242/e3e58fccdbd92a0c),
quite a few people care about seeing this fixed for Clojure 1.4. Could
someone in core please comment?

Best,
Constantine Vetoshev

Sean Corfield

unread,
Jan 12, 2012, 5:43:08 PM1/12/12
to cloju...@googlegroups.com
I think part of the problem is that there are several possible
solutions and no solid consensus in any particular direction?

Any code out there that is catching RuntimeException and unwrapping it
will be broken by both sneaky-throw and try-unwraps as they stand -
which might cause problems.

I'd lean toward try-unwraps but based on the existing RuntimeException
and allowing any such existing code to continue to work (by not
unwrapping if there's a catch that can catch RuntimeException) but
others may prefer a more complete fix.

If we're going to break that code, I think I'd prefer sneaky-throw so
that try remains "clean" (and so that Clojure code can't leak weird
wrapped exceptions to any enclosing non-Clojure code - I live in a
polyglot world so I care about that).

Sean

Constantine Vetoshev

unread,
Jan 12, 2012, 6:10:39 PM1/12/12
to cloju...@googlegroups.com
On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seanco...@gmail.com> wrote:
> Any code out there that is catching RuntimeException and unwrapping it
> will be broken by both sneaky-throw and try-unwraps as they stand -
> which might cause problems.

I think that fixing breaks introduced by either patch will be easy —
certainly much easier than discovering the problem while migrating to
Clojure 1.3, and then being forced to write workarounds everywhere.

> If we're going to break that code, I think I'd prefer sneaky-throw so
> that try remains "clean" (and so that Clojure code can't leak weird
> wrapped exceptions to any enclosing non-Clojure code - I live in a
> polyglot world so I care about that).

I also prefer sneaky-throw.

Kevin Downey

unread,
Jan 12, 2012, 7:55:12 PM1/12/12
to cloju...@googlegroups.com

I agree sneak-throw is the clear choice, if we could just someone from
core to pay attention.

> --
> You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
> To post to this group, send email to cloju...@googlegroups.com.
> To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.
>

--

Stuart Halloway

unread,
Jan 12, 2012, 9:39:35 PM1/12/12
to cloju...@googlegroups.com
On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seanco...@gmail.com> wrote:
Any code out there that is catching RuntimeException and unwrapping it
will be broken by both sneaky-throw and try-unwraps as they stand -
which might cause problems.

I think that fixing breaks introduced by either patch will be easy —
certainly much easier than discovering the problem while migrating to
Clojure 1.3, and then being forced to write workarounds everywhere.

If we're going to break that code, I think I'd prefer sneaky-throw so
that try remains "clean" (and so that Clojure code can't leak weird
wrapped exceptions to any enclosing non-Clojure code - I live in a
polyglot world so I care about that).

I also prefer sneaky-throw.

I agree sneak-throw is the clear choice, if we could just someone from
core to pay attention.

I also like sneaky-throw. I have questions:

1. The comment thread on the ticket (http://dev.clojure.org/jira/browse/CLJ-855) appears to end with open questions. Are these answered?

2. Does type-hinting to avoid reflection succeed as a workaround? If so we should document this somewhere for people who are stuck on existing versions.

Stu

Stuart Halloway
Clojure/core
http://clojure.com

Constantine Vetoshev

unread,
Jan 12, 2012, 10:29:27 PM1/12/12
to cloju...@googlegroups.com
On Thu, Jan 12, 2012 at 6:39 PM, Stuart Halloway
<stuart....@gmail.com> wrote:
> 2. Does type-hinting to avoid reflection succeed as a workaround? If so we
> should document this somewhere for people who are stuck on existing
> versions.

Type hints do help, at least in my use cases. Thank you for the suggestion, Stu.

To use my original example:

(defn broken-catch [filename]
(try (java.io.FileReader. filename)
(catch java.io.FileNotFoundException fnfe
"FileNotFoundException caught")
(catch RuntimeException ioe
(str "RuntimeException caught; cause: "
(.getCause ioe)))))

(defn working-catch [^String filename]
(try (java.io.FileReader. filename)
(catch java.io.FileNotFoundException fnfe
"FileNotFoundException caught")
(catch RuntimeException ioe
(str "RuntimeException caught; cause: "
(.getCause ioe)))))

This works as expected in Clojure 1.3.0:

user=> *clojure-version*
{:major 1, :minor 3, :incremental 0, :qualifier nil}
user=> (broken-catch "/etc/passwdXYZ")
"RuntimeException caught; cause: java.io.FileNotFoundException:
/etc/passwdXYZ (No such file or directory)"
user=> (working-catch "/etc/passwdXYZ")
"FileNotFoundException caught"

Best,
Constantine

Ben Smith-Mannschott

unread,
Jan 13, 2012, 12:08:08 PM1/13/12
to cloju...@googlegroups.com
On Fri, Jan 13, 2012 at 03:39, Stuart Halloway
<stuart....@gmail.com> wrote:
> On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seanco...@gmail.com>
> wrote:
>
> Any code out there that is catching RuntimeException and unwrapping it
>
> will be broken by both sneaky-throw and try-unwraps as they stand -
>
> which might cause problems.
>
>
> I think that fixing breaks introduced by either patch will be easy —
>
> certainly much easier than discovering the problem while migrating to
>
> Clojure 1.3, and then being forced to write workarounds everywhere.
>
>
> If we're going to break that code, I think I'd prefer sneaky-throw so
>
> that try remains "clean" (and so that Clojure code can't leak weird
>
> wrapped exceptions to any enclosing non-Clojure code - I live in a
>
> polyglot world so I care about that).
>
>
> I also prefer sneaky-throw.
>
>
> I agree sneak-throw is the clear choice, if we could just someone from
> core to pay attention.
>
>
> I also like sneaky-throw. I have questions:
>
> 1. The comment thread on the ticket
> (http://dev.clojure.org/jira/browse/CLJ-855) appears to end with open
> questions. Are these answered?

Q1. Reflector catches Exception, but seems most interested in
its cause. The cause, if Error is thrown directly, if present and
Exception it's possibly wrapped in an RTE and then thrown.

This implies to me that Reflector is expecting the code it's calling
at that point to throw wrapped exceptions. So maybe, it's expecting to
catch wrapped exceptions from Clojure code, but then why isn't it
catching RTE? If the exceptions it thinks might bubble up here aren't
coming from Clojure code (which wraps all checked exceptions), then
why is it blindly assuming the cause must be more important than the
actually thrown exception? It seems sloppy, but maybe I'm not
understanding the thinking behind it. Thus my initial question.

If I knew that the intent was to catch RTEs produced by Clojure
wrapping checked exceptions, then I could just sneaky-throw the caught
exception and be done with it.

Q2: The fact that Var.dissoc catches and then returns an exception
rather than throwing it still strikes me as very odd, though I think I
figured out that it doesn't matter. (TL;DR: scroll down a bit).

//-- Var.java
static IFn dissoc = new AFn() {
@Override
public Object invoke(Object c, Object k) {
try
{
return RT.dissoc(c, k);
}
catch(Exception e)
{
return Util.runtimeException(e);
}
}
};

//-- RT.java
static public Object dissoc(Object coll, Object key) {
if(coll == null)
return null;
return ((IPersistentMap) coll).without(key);
}

Presumably Var.dissoc is expected to behave like RT.dissoc (return the
new version of the PersistentMap), so why would it suddenly *return*
an Exception object? It's used once, in bindRoot(), which passes it to
the alterMeta:

//-- Var.java
//binding root always clears macro flag
synchronized public void bindRoot(Object root){
validate(getValidator(), root);
Object oldroot = this.root;
this.root = root;
++rev;
try
{
alterMeta(dissoc, RT.list(macroKey));
}
catch (Exception e)
{
throw Util.runtimeException(e);
}
notifyWatches(oldroot,this.root);
}

//-- AReference.java

synchronized public IPersistentMap alterMeta(IFn alter, ISeq args) {
_meta = (IPersistentMap) alter.applyTo(new Cons(_meta, args));
return _meta;
}


- The oddity is dissoc.invoke(c,k) returning, rather than throwing an Exception.
- This occurs if RT.dissoc(c, k) throws and Exception
- This occurs (most likely) if c doesn't implement IPersistentMap
- This means that the alterMeta method must have been called on an
AReference whose _meta is not an IPersistentMap
- But, that's impossible since we know statically taht
AReferenced._meta is always an IPersistentMap.

So, the oddity in Var.dissoc could only observed if the implementation
of IPersistentMap used for AReference._meta had an error in its
without method, causing it to throw an exception. That seems unlikely,
and even it occured it would be an internal error in Clojure (all bets
are off).

So, to get back to the original point: Var.dissoc is *odd*, but
probably harmless. I can find no good reason for its oddness, since I
think I've just established that the error scenario will not occur in
practice. So, following the principle of least astonishment, I'd
suggest it throw, rather than return, the exception.

So, I've answered Q2 myself to my satisfaction. I'd appreciate some
illumination of Q1 since my mind reading skills are lacking.

// Ben

> 2. Does type-hinting to avoid reflection succeed as a workaround? If so we
> should document this somewhere for people who are stuck on existing
> versions.
>
> Stu
>
> Stuart Halloway
> Clojure/core
> http://clojure.com
>

Kevin Downey

unread,
Jan 13, 2012, 12:41:21 PM1/13/12
to cloju...@googlegroups.com

Exceptions from java reflection are generally wrapped in some thing like invocation target exception

Ben Smith-Mannschott

unread,
Jan 13, 2012, 1:43:53 PM1/13/12
to cloju...@googlegroups.com
On Fri, Jan 13, 2012 at 18:41, Kevin Downey <red...@gmail.com> wrote:
> Exceptions from java reflection are generally wrapped in some thing like
> invocation target exception

Ah, that's a good thought, but wouldn't it make more sense to
specifically catch the specific exceptions thrown by reflection?

// ben

Herwig Hochleitner

unread,
Jan 16, 2012, 8:05:04 PM1/16/12
to cloju...@googlegroups.com
> 2. Does type-hinting to avoid reflection succeed as a workaround?

It does avoid the bug in the case where the wrapping is introduced by
Reflector.javaAFAIK there are a lot of other places, where such
wrapping might be introduced.


> 1. The comment thread on the ticket
> (http://dev.clojure.org/jira/browse/CLJ-855) appears to end with open
> questions. Are these answered?

Not really, because the only question that really is left unanswered is:
Do we allow the undoing of the checked-exceptions - ragequit patch
https://github.com/clojure/clojure/commit/8fda34e4c77cac079b711da59d5fe49b74605553
along with the breakage that will ensue.

The other issues (and reasons why that is desirable) might be obvious
from previous discussion, but I'll try to summarize for your
convenience.

When it was decided to have RuntimeExceptions everywhere in clojure,
it supposedly made life easier for polyglot users.

You didn't need to prepare for CheckedExceptions everywhere anymore.

Except when you had to, because of someone deciding to (throw
(Exception.)) in a clojure code.
Only now you have to pull special tricks to even be _able_ to catch
that Exception.

The reflection code only highlights that fact.

So: Do we want to undo the exception wrapping?
This is not as bad as it sounds, as we can get away with throwing
checked exceptions, without declaring them;
Even from plain Java; Also catching.

Otherwise we have picked a fight against corner cases. Is this the clojure.

Some more biased points:

## Keep it the way it is
- Play nice with java users most of the time
- Take a small performance hit
- Make life messy for clojure users just wanting the original exception
- Still won't solve all cases of people expecting an Exception and
getting it wrapped
-TODO: Lay out clear rules which exceptions to wrap, which ones to
leave, apply those rules to Reflector.java

## Sneakily throw (and catch) undeclared checked exceptions
- Don't play nice with java users, instead give them the tools to handle it
- Take no performance hit
- Give clojure users the exceptions they expect, straightforward semantics
- Might be a hard sell to the core team, who appeared to be very glad
to get rid of Checked Exceptions.
-TODO: Greenlight it

===============

I'm in favor of option two, especially since it's possible to give FNs a method:
somefn.invokeChecked<IOException>(arg);
to declare a checked exception on a function call, which seems fit for
an interface to a dynamic language.
where a checked exceptions might be thrown any time (but mostly won't).

===============

Punch line: If we don't do it now, we should definitely do it in 2.0
(it's called semantic versioning, maybe you've heard of it)

Hugo Duncan

unread,
Jan 19, 2012, 4:49:02 PM1/19/12
to Clojure Dev

On Jan 12, 9:39 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:

> 2. Does type-hinting to avoid reflection succeed as a workaround? If so we should document this somewhere for people who are stuck on existing versions.

That only works on code under our control. I just hit this issue with
clojure.core/load wrapping the FileNotFoundException being thrown when
passed a non-existing path.

Hugo

Paul Stadig

unread,
Feb 10, 2012, 11:17:14 AM2/10/12
to cloju...@googlegroups.com

Echoing Hugo here.

Currently, if you write something like

(defn boom []
  (throw (java.io.IOException.)))

what gets thrown is an unwrapped IOException. It is also the case that you may have type hinted code that calls some method that throws an IOException, then what gets thrown is an unwrapped IOException.

~~~
FROM THE JAVA SIDE
~~~

The bytecode that is generated will throw an unwrapped IOException (a checked exception), yet the IFn interface does not declare any throws clause.

From the Java side, you could be in a situation where you'd like to catch an IOException, but your code will not compile.  The Java complier considers your catch clause unreachable, since it is trying to catch a checked exception where that checked exception is not declared.

It seems like calling Clojure code from Java was better off when IFn had a "throws Exception" clause, because then it would at least be possible to catch the Exception.

~~~
FROM THE CLOJURE SIDE
~~~

Since the "throws Exception" clause was removed throughout Clojure's Java codebase, it is now necessary to catch and "handle" checked exceptions by wrapping them in RuntimeExceptions.

Say you want to catch an IOException in your Clojure code, there's just no way to know for sure if you'll get an IOException or an IOException wrapped in a RuntimeException (in the case of untype hinted code that someone else wrote), so you have to deal with both cases.

It seems like calling Clojure and/or Java code from Clojure was better off when IFn and the Reflector had "throws Exception" clauses, and did not wrap checked exceptions with RuntimeExceptions.

~~~
PROPOSAL
~~~

Revert commit 8fda. This will have only positive effects AFAIKT. This will make it possible to catch checked exceptions when calling Clojure code from Java, and it will eliminate the need to deal with the RTE wrapping case in the Clojure code. Any Clojure code that has been adjusted to handle both the wrappped and unwrapped cases will continue to work without any problem.

Please forgive me if I'm missing something crucial as to the rationale for commit 8fda. I did search around to try to find out what the rationale was, but I would be glad to hear that I am wrong.


Paul

Stuart Halloway

unread,
Feb 17, 2012, 12:44:35 PM2/17/12
to cloju...@googlegroups.com
I still prefer sneaky-throw, but don't have a final answer yet. The Java side of that isn't great, but is mitigated by having Java callers program to interfaces.  (How much code is being written to call IFn directly from Java? This is a serious question, not an attempt to dismiss.)

We do plan to address this in some way before releasing.

Stuart Sierra

unread,
Feb 18, 2012, 4:18:01 PM2/18/12
to cloju...@googlegroups.com
On Friday, February 17, 2012 12:44:35 PM UTC-5, Stuart Halloway wrote:
  (How much code is being written to call IFn directly from Java? This is a serious question, not an attempt to dismiss.)
 

Java code calling Clojure fns by name is using Var.invoke. Don't know who's doing this, but it's the only way to call into Clojure from Java without adding extra interfaces or gen-class.

-S

Cosmin Stejerean

unread,
Feb 18, 2012, 4:56:22 PM2/18/12
to cloju...@googlegroups.com
I've done this before because it's the easiest way to quickly invoke
Clojure from Java and doesn't require too much thinking.


- Cosmin

> --
> You received this message because you are subscribed to the Google Groups
> "Clojure Dev" group.

> To view this discussion on the web visit
> https://groups.google.com/d/msg/clojure-dev/-/J2sp3aMtxk8J.


>
> To post to this group, send email to cloju...@googlegroups.com.
> To unsubscribe from this group, send email to
> clojure-dev...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/clojure-dev?hl=en.

--
Cosmin Stejerean
http://offbytwo.com

Chas Emerick

unread,
Feb 18, 2012, 6:05:51 PM2/18/12
to cloju...@googlegroups.com
I write calls to IFns in Java on perhaps a monthly or bimonthly basis.  However, I rarely bother with exception handling.  Such calls are most often involved in bootstrap or other coarse-grained integration scenarios; if something goes wrong, I'm mostly SOL anyway, so the Java code involved either throws or catches Exception (if using Clojure 1.3), or does nothing re: error handling at all otherwise.

- Chas

Sean Corfield

unread,
Feb 18, 2012, 6:58:35 PM2/18/12
to cloju...@googlegroups.com
On Sat, Feb 18, 2012 at 1:18 PM, Stuart Sierra
<the.stua...@gmail.com> wrote:
> Java code calling Clojure fns by name is using Var.invoke. Don't know who's
> doing this, but it's the only way to call into Clojure from Java without
> adding extra interfaces or gen-class.

This is how we do it at World Singles (Var.invoke) and we're relying
heavily on it for integrating Clojure into our platform.


--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."

Sean Corfield

unread,
Feb 18, 2012, 7:00:17 PM2/18/12
to cloju...@googlegroups.com
On Sat, Feb 18, 2012 at 3:58 PM, Sean Corfield <seanco...@gmail.com> wrote:
> This is how we do it at World Singles (Var.invoke) and we're relying
> heavily on it for integrating Clojure into our platform.

For exception handling, we either catch it in the Clojure code or, if
it escapes to the calling code, treat it as a generic "Sorry,
something went wrong! Our team have been notified..." so we don't much
care about wrapped / unwrapped exceptions outside Clojure, only inside
Clojure.

Paul Stadig

unread,
Feb 28, 2012, 9:46:24 PM2/28/12
to cloju...@googlegroups.com
From the Java side, programming against interfaces doesn't help when the interfaces don't declare any exceptions, but throw checked exceptions, and you want catch them.

The Clojure side is pretty bad, too. Here's a field report. We finally had the resources to take a good look at upgrading to 1.3, and we ran into lots of problems with the RTE wrapping. We do a lot of Java interop. The problem with wrapping exceptions in RuntimeException is that it changes the semantics of exceptions. When someone throws EOFException they mean "Holy crap! EOFException!" not "RuntimeException RuntimeException EOFException" As I said before, it isn't possible to know from the Clojure side whether code you call might go through the Reflector and have its exceptions wrapped.

You would think that you could just catch Exception and use clojure.stacktrace/root-cause, except in our case we're using Futures, and when an exception occurs it gets wrapped in an ExecutionException or a CancellationException, and we want to find out whether we have an ExecutionException or a CancellationException. If we use root-cause it unwraps all the way down past the ExecutionExceptionn/CancellationException. We could certainly just unwrap the layers of RuntimeExceptions, but what if we wanted to catch a RuntimeException?

It seems like every one of our catch clauses is having to get updated, and they are having to be updated to do something like catch Exception then some kind of cond based on the class of the root cause, which is really just duplicating what a catch is supposed to do in the first place.

In the general case, we can't be sure whether we will get a wrapped exception or an unwrapped exception. It's not fair for Clojure to make assumptions about how easy it will be for users to unwrap since in some cases we may not want to unwrap all the way down, or we may want to catch a RuntimeException.

And checked exceptions are a fabrication of the Java compiler, so I'm still at a loss to understand why this change was made. It makes Clojure's Java code not have to declare that they throw Exception? Is there some benefit to either the Java side or the Clojure side or both. I would like to understand the tradeoffs.

It's possible that something like sneaky throw or "chucking" exceptions would make things better from the Clojure side to not have to deal with if wrapping is occuring or how far down you want to unwrap. However, I'm still in favor of reverting this commit. I see only breakage and no benefit.

Is there some benefit?

Is there a better way to deal with this in my Clojure code?


Paul

Laurent PETIT

unread,
Feb 29, 2012, 10:39:05 AM2/29/12
to cloju...@googlegroups.com


2012/2/29 Paul Stadig <pa...@stadig.name>

From the Java side, programming against interfaces doesn't help when the interfaces don't declare any exceptions, but throw checked exceptions, and you want catch them.

The Clojure side is pretty bad, too. Here's a field report. We finally had the resources to take a good look at upgrading to 1.3, and we ran into lots of problems with the RTE wrapping. We do a lot of Java interop. The problem with wrapping exceptions in RuntimeException is that it changes the semantics of exceptions. When someone throws EOFException they mean "Holy crap! EOFException!" not "RuntimeException RuntimeException EOFException" As I said before, it isn't possible to know from the Clojure side whether code you call might go through the Reflector and have its exceptions wrapped.

You would think that you could just catch Exception and use clojure.stacktrace/root-cause, except in our case we're using Futures, and when an exception occurs it gets wrapped in an ExecutionException or a CancellationException, and we want to find out whether we have an ExecutionException or a CancellationException. If we use root-cause it unwraps all the way down past the ExecutionExceptionn/CancellationException. We could certainly just unwrap the layers of RuntimeExceptions, but what if we wanted to catch a RuntimeException?

I'm having the same exact problem in Clojure code for CCW's leiningen integration, and trying to handle aether's ( a java library for dealing with maven repositories ).

At the point in code where I try to handle the exceptions to report relevant error messages to the user, I have a mess of exceptions sometimes being wrapped in one or more RuntimeExceptions, plus a bunch of cases where the RuntimeException is really the one to be considered, etc. (so I cannot just get rid of RuntimeExceptions until I reach some non-RuntimeException which I would consider to  be some 'semantically interesting' exception ...)

My take is that either those wrapping by RuntimeExceptions go away, either a specific RuntimeExceptionWrapper is created so that we can reliably peel the onion until we reach the interesting part.
 

It seems like every one of our catch clauses is having to get updated, and they are having to be updated to do something like catch Exception then some kind of cond based on the class of the root cause, which is really just duplicating what a catch is supposed to do in the first place.

In the general case, we can't be sure whether we will get a wrapped exception or an unwrapped exception. It's not fair for Clojure to make assumptions about how easy it will be for users to unwrap since in some cases we may not want to unwrap all the way down, or we may want to catch a RuntimeException.

And checked exceptions are a fabrication of the Java compiler, so I'm still at a loss to understand why this change was made. It makes Clojure's Java code not have to declare that they throw Exception? Is there some benefit to either the Java side or the Clojure side or both. I would like to understand the tradeoffs.

It's possible that something like sneaky throw or "chucking" exceptions would make things better from the Clojure side to not have to deal with if wrapping is occuring or how far down you want to unwrap. However, I'm still in favor of reverting this commit. I see only breakage and no benefit.

Is there some benefit?

Is there a better way to deal with this in my Clojure code?


Paul

Stuart Halloway

unread,
Feb 29, 2012, 4:38:01 PM2/29/12
to cloju...@googlegroups.com
And checked exceptions are a fabrication of the Java compiler, so I'm still at a loss to understand why this change was made. It makes Clojure's Java code not have to declare that they throw Exception? Is there some benefit to either the Java side or the Clojure side or both. I would like to understand the tradeoffs.

It's possible that something like sneaky throw or "chucking" exceptions would make things better from the Clojure side to not have to deal with if wrapping is occuring or how far down you want to unwrap. However, I'm still in favor of reverting this commit. I see only breakage and no benefit.

Is there some benefit?

Some of the original conversation is at http://clojure-log.n01se.net/date/2011-03-21.html. But that does not seem to be the beginning. Chas, do you (or anybody else) have links to more of the conversation?


Is there a better way to deal with this in my Clojure code?

1.4 beta 2 with sneaky throw is sliding through the maven gullet as we speak. Once it is out, please test it and let us know what problems it causes/solves.

Paul Stadig

unread,
Feb 29, 2012, 4:58:43 PM2/29/12
to cloju...@googlegroups.com
On Wed, Feb 29, 2012 at 4:38 PM, Stuart Halloway <stuart....@gmail.com> wrote:
Some of the original conversation is at http://clojure-log.n01se.net/date/2011-03-21.html. But that does not seem to be the beginning. Chas, do you (or anybody else) have links to more of the conversation?

LOL! I guess I was involved in that original discussion, but I swear I don't remember it! :)
Is there a better way to deal with this in my Clojure code?

1.4 beta 2 with sneaky throw is sliding through the maven gullet as we speak. Once it is out, please test it and let us know what problems it causes/solves.

Great! I think I can live with sneaky throw, since I guess it is essentially what would happen if more of the Clojure compiler/runtime were written in Clojure.

This was going to be a huge blocker to Sonian being able to upgrade to 1.4. The wrapping was the root of our problems. I'm glad that it is worked out. We'll be working against 1.4-beta2. Are there concrete plans for when there might be a final 1.4 release?

Thanks to everyone involved!


Paul

Chas Emerick

unread,
Feb 29, 2012, 5:05:30 PM2/29/12
to cloju...@googlegroups.com

On Feb 29, 2012, at 4:38 PM, Stuart Halloway wrote:

And checked exceptions are a fabrication of the Java compiler, so I'm still at a loss to understand why this change was made. It makes Clojure's Java code not have to declare that they throw Exception? Is there some benefit to either the Java side or the Clojure side or both. I would like to understand the tradeoffs.

It's possible that something like sneaky throw or "chucking" exceptions would make things better from the Clojure side to not have to deal with if wrapping is occuring or how far down you want to unwrap. However, I'm still in favor of reverting this commit. I see only breakage and no benefit.

Is there some benefit?

Some of the original conversation is at http://clojure-log.n01se.net/date/2011-03-21.html. But that does not seem to be the beginning. Chas, do you (or anybody else) have links to more of the conversation?

I don't recall the timeline exactly, but I recall Rich and I talking about Java => Clojure interop at a meetup once, which spurred this:


Prior to 1.3's elision of the declared throws, calling into Clojure from Java was dreadful, and it would bubble up throughout an application — especially unpleasant when the fn being invoked was known to not throw anything that would be checked, even in Java-land.  Post-1.3, points of interop can be at any granularity without paying an unnecessary-checked-exception tax; perhaps not a problem that Clojure should have had to solve, but a real one nonetheless.

Hopefully that helps with the motivation question?

- Chas

Stuart Halloway

unread,
Feb 29, 2012, 5:14:18 PM2/29/12
to cloju...@googlegroups.com
Is there a better way to deal with this in my Clojure code?

1.4 beta 2 with sneaky throw is sliding through the maven gullet as we speak. Once it is out, please test it and let us know what problems it causes/solves.

Great! I think I can live with sneaky throw, since I guess it is essentially what would happen if more of the Clojure compiler/runtime were written in Clojure.

This was going to be a huge blocker to Sonian being able to upgrade to 1.4. The wrapping was the root of our problems. I'm glad that it is worked out. We'll be working against 1.4-beta2. Are there concrete plans for when there might be a final 1.4 release?

Thanks to everyone involved!

I will hold my warm fuzzy feeling til I hear back from your testing... :-)

This was the most critical issue I was aware of blocking 1.4. What's the next most critical?

Stu

Aaron Cohen

unread,
Feb 29, 2012, 5:22:35 PM2/29/12
to cloju...@googlegroups.com
I wonder if it would be nice to add a convenience function in RT to
help with catching the sneakily thrown Exceptions on the java side.

Ie:

public static <T extends Throwable> Object
declareExceptionsAndInvoke(IFn callable, Object... args) throws T {
callable.applyTo(Arrays.asList(args));
}

This would allow Java-side code to do (normally it can't catch
Exception, because javac doesn't believe it to be thrown within the
catch block):

void example() {
Var theNs = RT.var("clojure.core", "the-ns"); // throws Exception
if ns doesn't exist

try {
RT.<Exception>declareExceptionsAndInvoke(theNs, "IdontExist");
}
catch(Exception e) {
// I can catch this
}
}

Hugo Duncan

unread,
Feb 29, 2012, 10:51:54 PM2/29/12
to cloju...@googlegroups.com
Stuart Halloway <stuart....@gmail.com> writes:

> This was the most critical issue I was aware of blocking 1.4. What's the next most critical?

Maybe not so critical, but still nice to have:

CLJ-103 Incorrect error with if-let

CLJ-196 *file* returns "NO_SOURCE_PATH", but the doc says it should be nil

CLJ-683 broken example in ns docstring

CLJ-917 clojure.core/definterface is not included in the API docs

CLJ-835 defmulti doc string doesn't mention needing to be passed a var for the value of :hierarchy

CLJ-788 Add source and line members and getters to CompilerException

CLJ-939 Exceptions thrown in the top level ns form are reported without file or line number

CLJ-940 Passing a non-sequence to refer :only results in uninformative exception


And my own personal favourite:

CLJ-860 Add ability to disable locals clearing

Andy Fingerhut

unread,
Feb 29, 2012, 11:13:36 PM2/29/12
to cloju...@googlegroups.com

On Feb 29, 2012, at 7:51 PM, Hugo Duncan wrote:

> Stuart Halloway <stuart....@gmail.com> writes:
>
>> This was the most critical issue I was aware of blocking 1.4. What's the next most critical?
>

Also really up to Clojure/core team whether these are critical...

These are already marked as screened:

CLJ-886 java.io/do-copy can garble multibyte characters

CLJ-852 IllegalArgumentException thrown when defining a var whose value is calculated with a primitive fn

CLJ-369 Check for invalid interface method names

CLJ-924 Error reporting of invalid digit in unicode character literal


These have unscreened patches that fix bugs:

CLJ-870 clojure.string/replace behaves unexpectedly when \ or $ are part of the result string
(the most recent patch also fixed CLJ-753 and CLJ-905)

CLJ-881 Problem with the "cl-format" function from the clojure.pprint
(the bug is that cl-format throws an exception for a perfectly legal call, data-dependent)

CLJ-931 Syntactically broken clojure.tests/are tests succeed


It sounds like the following would be nice so that cljs.core and all other namespaces can have a useful / symbol:

CLJ-873 Allow the function / to be referred to in namespaces other than clojure.core


Andy

pmbauer

unread,
Mar 1, 2012, 1:22:22 AM3/1/12
to cloju...@googlegroups.com
For Java client code, I'd personally prefer a little helper shim like this:

public static <T extends Throwable> void thrown(Class<T> c) throws T {}

Then in code:
 Var slurp = RT.var("clojure.core", "slurp");
 try {
   thrown(FileNotFoundException.class);
   System.out.println(slurp.invoke("/etc/Idontexist"));
 } catch (FileNotFoundException fnfe) {}

Java's generic method invocation syntax is really messy and a 'declareExceptionsAndInvoke' requires wrapping all the args in an object array.

Paul Stadig

unread,
Mar 1, 2012, 6:42:09 PM3/1/12
to cloju...@googlegroups.com
On Wed, Feb 29, 2012 at 5:14 PM, Stuart Halloway <stuart....@gmail.com> wrote:
Is there a better way to deal with this in my Clojure code?

1.4 beta 2 with sneaky throw is sliding through the maven gullet as we speak. Once it is out, please test it and let us know what problems it causes/solves.

Great! I think I can live with sneaky throw, since I guess it is essentially what would happen if more of the Clojure compiler/runtime were written in Clojure.

This was going to be a huge blocker to Sonian being able to upgrade to 1.4. The wrapping was the root of our problems. I'm glad that it is worked out. We'll be working against 1.4-beta2. Are there concrete plans for when there might be a final 1.4 release?

Thanks to everyone involved!

I will hold my warm fuzzy feeling til I hear back from your testing... :-)

I worked with 1.4-beta2, and found a case where the Reflector double unwraps an exception when calling an instance method with one or more arguments (it doesn't happen in the no argument case).

So with my Future example, if my call to deref a Future happens reflectively and the Future had thrown an exception, the exception would be wrapped in an ExecutionException, and that would get wrapped in an InvocationTargetException, and the Reflector code would unwrap both even though it should only be unwrapping the InvocationTargetException.

I've opened an issue (http://dev.clojure.org/jira/browse/CLJ-942) and attached a patch with a test and the fix.

I also moved the ReflectorTryCatchFixture class into the java dir for tests. Currently it is being included in the clojure jar, even though it is really part of the test suite.


Paul

Reply all
Reply to author
Forward
0 new messages