[HN Gopher] JEP draft: Exception handling in switch
___________________________________________________________________
JEP draft: Exception handling in switch
Author : vips7L
Score : 101 points
Date : 2024-04-19 16:22 UTC (6 hours ago)
(HTM) web link (openjdk.org)
(TXT) w3m dump (openjdk.org)
| atomicnumber3 wrote:
| This will really help using streams and just being
| expression/HOF-oriented in general, I often find myself writing
| custom FunctionalInterface's just to capture an exception instead
| of writing a Consumer/Function/etc. maybe this will help ease
| that a bit. I still wish there was a way to just "bail out"
| execution of a lambda like you can a for loop if the function the
| lambda is used in is declared to throw the exception in question.
|
| But I often find that I'm not thinking functional enough if I
| find myself trying to do that. But also sometimes I'm forced to
| by surrounding APIs etc.
| tadfisher wrote:
| My approach lately is either:
|
| - Ban exception propagation altogether: in Kotlin, wrap the
| computation in 'runCatching' to get a Result<T>; or in Java,
| use result4j [1] which provides similar functionality.
|
| - Let exceptions propagate, catching them at as high a level as
| possible.
|
| Which one to go with depends on the type of program I'm
| writing. If it's a GUI tool, I go with the first approach,
| because I want to display errors to the user. If it's a CLI
| tool or backend service, I go with the second option, because I
| want to short-circuit the program as soon as possible to avoid
| potential logic errors.
|
| [1]: https://github.com/sviperll/result4j
| groestl wrote:
| I tend to do the former only when writing executors, task
| queues, retry strategies, batch handlers, and the likes,
| otherwise rely on 2) (the UI is just another "high level")
| cogman10 wrote:
| I mostly work in the backend and yeah, the second approach is
| what I value the most.
|
| Particularly because it can provide a ton of useful context
| (if logging is correctly setup) and doesn't end up littering
| the code with try/catch blocks and stuttered logging.
| bedobi wrote:
| using RunCatching is inadvisable, it catches too much and can
| break lots of things in hard to detect ways
|
| https://github.com/sksamuel/tabby/blob/0fa37638712efd6b059f2.
| ..
|
| (though I recommend using the Arrow library, it has great
| types for fixing all the countless foot guns Kotlin devs
| insist on adding to the language and native libraries)
| ackfoobar wrote:
| > Let exceptions propagate, catching them at as high a level
| as possible.
|
| This can't be done cleanly in Java.
|
| Checked exception can encode union types, but this extra
| power is not complemented anywhere else in Java's type
| system. E.g. in a `Consumer` lambda passed to `forEach`,
| Java's checked exception forces you to convert that to a
| RuntimeException.
| PhilipRoman wrote:
| I'd say it is due to a lack of variadic type parameters.
| You can write something like this: <T, X
| extends Throwable> void forEach(ThrowingConsumer<T, X> f)
| throws X;
|
| But the only way to have multiple exception types without
| losing static type checking is to have multiple X
| parameters, like X1, X2, X3... (with unused parameters
| being set to some subtype of RuntimeException so that they
| do not participate in checked exception handling).
|
| Whether or not it is worth to write this madness just to
| satisfy one's OCD is up to the reader.
| ackfoobar wrote:
| > it is due to a lack of variadic type parameters
|
| To be pedantic, it is due to union types "not
| complemented anywhere else in Java's type system". Adding
| variadic type params is _a_ way to solve this. Another
| way is, of course, to support union types.
|
| > with unused parameters being set to some subtype of
| RuntimeException
|
| Or the `Nothing` type (`never` in TypeScript), where `A |
| Nothing = A`.
| wiseowise wrote:
| > - Ban exception propagation altogether: in Kotlin, wrap the
| computation in 'runCatching' to get a Result<T>; or in Java,
| use result4j [1] which provides similar functionality.
|
| 'runCatching' was never intended for user code. It was made
| for internal use in coroutines machinery and when community
| complained they made it public. But it is still a half-baked
| API that is better to avoid (catching CancellationException,
| doesn't compose well in general).
|
| The whole runCatching/billions of Result<T> copies are worse
| than Go's "if err !=".
|
| Unless JetBrains rolls out language support for something
| like Rust's Result, exceptions are superior and Kotlin
| community would better off embracing them instead of trying
| to be different for the sake of being different.
|
| https://web.mit.edu/rust-
| lang_v1.25/arch/amd64_ubuntu1404/sh...
| PaulHoule wrote:
| Already you can uncheck exceptions by wrapping them in a
| function like the ones defined here
|
| https://paulhoule.github.io/pidove/apidocs/com/ontology2/pid...
|
| that is
| something.stream().map(unchecked(SomeClass::methodThatThrows))
| Groxx wrote:
| > case throws Exception e -> { log(e); yield score(0); }
|
| ... and this is why people so frequently break thread interrupts
| in Java. most Java sample code (and even official-feeling docs
| like this) violates basic exception hygiene.
|
| ---
|
| edit: that aside, I can see lots of uses for this pattern, and I
| like the clear scoping quite a bit. Seems like a good idea on the
| surface at the very least - I don't have enough experience here
| to really make a "good idea or no" claim.
| gizmo686 wrote:
| This complaint is not really about the proposed change, it is a
| longstanding problem with Java. As long as you treat exceptions
| with the sane care in switch statements as you do in try
| statements, this doesn't introduce any new problems.
| bedobi wrote:
| > it is a longstanding problem with Java
|
| it's, along with null pointers, probably the no 1 biggest
| foot gun in the language, responsible for countless bugs in
| pretty much every app ever written in the language, and it's
| treated as some unaddressed elephant in the room we don't
| talk about or consider alternatives to
| PaulHoule wrote:
| It is easy to put the right behavior in a function wrapper
| like
|
| https://github.com/paulhoule/pidove/blob/97f8ec697d2890f13b
| a...
| dgellow wrote:
| C# added nullable reference types a few years ago. Is there
| an equivalent in Java? That makes dealing with null pretty
| much a non-issue
|
| https://learn.microsoft.com/en-us/dotnet/csharp/nullable-
| ref...
| grishka wrote:
| > and this is why people so frequently break thread interrupts
| in Java.
|
| By the way. I've been writing predominantly Java over the last
| 15 years and I absolutely hate it that InterruptedException is
| checked. It gets in the way All. The. Damn. Time. All for those
| 0.1% of cases when you need to be able to cancel a blocking
| operation from another thread.
| agumonkey wrote:
| Next: handling classes in functions.
| sgammon wrote:
| I like it. `switch throws` looks like a really clean construct.
| mirekrusin wrote:
| I don't agree with reasoning to not call it "case catch ...":
| case catch would be asking "did it evaluate to something that
| catches this exception?", which doesn't make sense.
|
| Interpretation as "did it evaluate to something that would be
| catched like this?" makes perfect sense and is way more
| intuitive. Even "catches" would be better but why introducing new
| keyword?
|
| "throws" is way too close to an action of throwing.
| extraduder_ire wrote:
| From my reading, they wanted it to match the sort of grammar
| that type constructor/type cases have. (looks like a
| declaration) I have not read the mailing list discussion to see
| what other alternatives were suggested.
|
| My kneejerk reaction would be to prefer something like "threw"
| in place of "throws", but I'm sure there's a reason not to.
| lolinder wrote:
| > I'm sure there's a reason not to
|
| Language designers try to avoid adding new keywords if at all
| possible, especially ones that are short and common. Doing so
| requires either breaking thousands of projects and APIs or
| adding a bunch of complexity to the grammar and syntax
| highlighting tooling (so threw is only a keyword in some
| contexts but not others).
| mrighele wrote:
| You're matching with the result of the expression inside the
| switch, and in case of an exception the result is something
| being throw, not something being catched, so the chosen
| approach seems correct to me.
|
| given the code Future<Box> f = ...
| switch (f.get()) { case Box(String s) when
| isGoodString(s) -> score(100); case Box(String s)
| -> score(50); case null
| -> score(0); case throws CancellationException ce
| -> ...ce... case throws ExecutionException ee
| -> ...ee... case throws InterruptedException ie
| -> ...ie... };
|
| I read the above as
|
| "if f.get() is a Box(...) then ..."
|
| "if f.get() is null then ..."
|
| "if f.get() throws ExecutionException then ..."
| mirekrusin wrote:
| catch clauses in try/catch are also matching something being
| thrown.
|
| People don't have problem reading try/catch, are used to it,
| it's already there, semantics match - why complicate things?
|
| Case is better read as "captures ... [when ...]" and "case
| catch" simply means capturing an exception - the same way as
| try/catch does it.
|
| If you flip it around - if somebody would do an experiment
| where they'd ask 100 developers to imagine that java supports
| catching exceptions in switch statements my bet is that
| almost all, if not all would write it as "case catch ...".
|
| And there is really nothing fundamentally wrong with that.
| You can't catch catch handler, you can only catch an
| exception.
| speed_spread wrote:
| Further bikeshedding this, since the JEP admits regular
| clauses and Exception clauses should be kept separate as they
| are treated differently, we could as well retain the original
| catch syntax: switch (f.get()) {
| case Box(String s) when isGoodString(s) -> score(100);
| catch InterruptedException ie -> ...ie...
| };
|
| Current try/catch gymnastics are laborious, requiring blocks
| making usage unwieldy in otherwise-one-line lambdas.
| Requiring "case throws" is yet more useless syntax inflation.
| It would be nice to keep things streamlined this once.
| grishka wrote:
| English isn't my native language but I read that as "in case it
| throws this type of exception" so "case throws" makes perfect
| sense to me.
| pizlonator wrote:
| Super cool.
|
| Best part about this language proposal format is how it spells
| out goals and non-goals so clearly. Having the non-goals, and
| dialing them in, is really great.
___________________________________________________________________
(page generated 2024-04-19 23:01 UTC)