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 ;)
Instead of throwing RuntimeException, throw a special Clojure subclass of RuntimeException. try/catch can always unwrap the special subclass, but not other wrapped exceptions.
> Instead of throwing RuntimeException, throw a special Clojure subclass of > RuntimeException. try/catch can always unwrap the special subclass, but not > other wrapped exceptions.
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)
On Wed, Oct 12, 2011 at 10:27 AM, Sean Corfield <seancorfi...@gmail.com> wrote: > On Wed, Oct 12, 2011 at 5:52 AM, Stuart Sierra > <the.stuart.sie...@gmail.com> wrote: >> 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.
> 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)
> -- > You received this message because you are subscribed to the Google Groups "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to clojure-dev+unsubscribe@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?
> Instead of throwing RuntimeException, throw a special Clojure subclass of > RuntimeException. try/catch can always unwrap the special subclass, but not > other wrapped exceptions.
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); }
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.
On Mon, Dec 12, 2011 at 10:13 AM, Constantine Vetoshev
<gepar...@gmail.com> wrote: > 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
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
> -- > You received this message because you are subscribed to the Google Groups "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to clojure-dev+unsubscribe@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?
> 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?
On Tue, Dec 13, 2011 at 06:43, Constantine Vetoshev <gepar...@gmail.com> wrote: > On Dec 12, 2011, at 13:03, Kevin Downey wrote: >> On Mon, Dec 12, 2011 at 10:13 AM, Constantine Vetoshev >> <gepar...@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?
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.)
> -- > You received this message because you are subscribed to the Google Groups "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to clojure-dev+unsubscribe@googlegroups.com. > For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.
On Mon, Dec 12, 2011 at 10:13 AM, Constantine Vetoshev
<gepar...@gmail.com> wrote: > 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.
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/239975607...), quite a few people care about seeing this fixed for Clojure 1.4. Could someone in core please comment?
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
On Thu, Jan 12, 2012 at 2:12 PM, Constantine Vetoshev
<gepar...@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/239975607...), > quite a few people care about seeing this fixed for Clojure 1.4. Could > someone in core please comment?
On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seancorfi...@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).
<gepar...@gmail.com> wrote: > On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seancorfi...@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.
> -- > You received this message because you are subscribed to the Google Groups "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to clojure-dev+unsubscribe@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?
>> On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seancorfi...@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.
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.
<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.
Type hints do help, at least in my use cases. Thank you for the suggestion, Stu.
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).
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:
- 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.
> 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.
> -- > You received this message because you are subscribed to the Google Groups > "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to > clojure-dev+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/clojure-dev?hl=en.
Exceptions from java reflection are generally wrapped in some thing like invocation target exception On Jan 13, 2012 9:08 AM, "Ben Smith-Mannschott" <bsmith.o...@gmail.com> wrote:
> On Fri, Jan 13, 2012 at 03:39, Stuart Halloway > <stuart.hallo...@gmail.com> wrote: > > On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seancorfi...@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.
> 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).
> 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:
> - 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.
> > -- > > You received this message because you are subscribed to the Google Groups > > "Clojure Dev" group. > > To post to this group, send email to clojure-dev@googlegroups.com. > > To unsubscribe from this group, send email to > > clojure-dev+unsubscribe@googlegroups.com. > > For more options, visit this group at > > http://groups.google.com/group/clojure-dev?hl=en.
> -- > You received this message because you are subscribed to the Google Groups > "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to > clojure-dev+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/clojure-dev?hl=en.
On Fri, Jan 13, 2012 at 18:41, Kevin Downey <redc...@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?
> On Jan 13, 2012 9:08 AM, "Ben Smith-Mannschott" <bsmith.o...@gmail.com> > wrote:
>> On Fri, Jan 13, 2012 at 03:39, Stuart Halloway >> <stuart.hallo...@gmail.com> wrote: >> > On Thu, Jan 12, 2012 at 2:43 PM, Sean Corfield <seancorfi...@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.
>> 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).
>> 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:
>> - 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.
>> > -- >> > You received this message because you are subscribed to the Google >> > Groups >> > "Clojure Dev" group. >> > To post to this group, send email to clojure-dev@googlegroups.com. >> > To unsubscribe from this group, send email to >> > clojure-dev+unsubscribe@googlegroups.com. >> > For more options, visit this group at >> > http://groups.google.com/group/clojure-dev?hl=en.
>> -- >> You received this message because you are subscribed to the Google Groups >> "Clojure Dev" group. >> To post to this group, send email to clojure-dev@googlegroups.com. >> To unsubscribe from this group, send email to >> clojure-dev+unsubscribe@googlegroups.com. >> For more options, visit this group at >> http://groups.google.com/group/clojure-dev?hl=en.
> -- > You received this message because you are subscribed to the Google Groups > "Clojure Dev" group. > To post to this group, send email to clojure-dev@googlegroups.com. > To unsubscribe from this group, send email to > clojure-dev+unsubscribe@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/clojure-dev?hl=en.
> 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.
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)
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.
On Thu, Jan 19, 2012 at 4:49 PM, Hugo Duncan <duncan.h...@gmail.com> wrote:
> 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
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.
> On Thu, Jan 19, 2012 at 4:49 PM, Hugo Duncan <duncan.h...@gmail.com> wrote:
> 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
> 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
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.
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.