25 ms·
JEP draft: Exception handling in switch
- atomicnumber3 3y agoThis 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 3y agoMy 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 https://github.com/sviperll/result4j
- groestl 3y agoI 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 3y agoI 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 3y agousing RunCatching is inadvisable, it catches too much and can break lots of things in hard to detect ways https://github.com/sksamuel/tabby/blob/0fa37638712efd6b059f280f95a8a2f87c95af89/tabby-fp/src/main/kotlin/com/sksamuel/tabby/results/catching.kt#L9 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 3y ago> 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 3y agoI'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 3y ago> 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 3y ago> - 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/share/doc/rust/html/std/result/index.html#the-question-mark-operator- https://web.mit.edu/rust-lang_v1.25/arch/amd64_ubuntu1404/sh...
- rileymichael 3y agoI have a hard time agreeing that exceptions (as they exist in kotlin, unchecked) are superior. There is no way to communicate (outside of docs) to callers that a function is possibly unsafe and that they should handle it. Good callout on the stdlib `Result`, a lot of people aren't aware of the `CancellationException` issue. Another pain point is that the error is constrained to `Throwable`, which is rather obtuse for general business logic (and you're likely generating stack traces needlessly unless you disable `writableStackTrace` on your custom throwables). I'm a fan of https://github.com/michaelbull/kotlin-result https://github.com/michaelbull/kotlin-result which has a fantastic API and supports monad comprehension which helps avoid the "arrowheads" from not having a built in operator.
- vips7L 3y agoThat result library doesn’t look thread safe at all. I would be extremely cautious using it.
- michaelbull 3y agoAuthor here. I have no idea what you could possibly mean with this comment. The coroutineBinding implementation correctly uses the coroutines API for parallel decomposition of Result bindings, exactly how the Kotlin Coroutines guide tells you to, backed by a Mutex[1]. The coroutineBinding isn't even the main selling point of the library, you can use it without using this feature entirely. Please could you elaborate on what "looking thread safe" means to you? The only portion of the library that supports concurrency *is* thread safe - the unit tests[2] prove it and the use of concurrency primitives such as Kotlin's Mutex[3] are indicative of this. I truly have no idea how you've judged the entirely of the library on whether it's "thread safe" when there is a single function (in an extension library, not the core one) that's related to concurrency and it is very clearly using concurrency primitives as intended. With regards to "being cautious using it", you don't need to be. The maven central statistics suggest its being downloaded 300,000 times per month. If there was something wrong with it, it's likely somebody would have raised this already given how frequently the project has been adopted over the last seven years. [1] https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result-coroutines/src/commonMain/kotlin/com/github/michaelbull/result/coroutines/CoroutineBinding.kt#L73 https://github.com/michaelbull/kotlin-result/blob/master/kot... [2] https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result-coroutines/src/commonTest/kotlin/com.github.michaelbull.result.coroutines/AsyncCoroutineBindingTest.kt https://github.com/michaelbull/kotlin-result/blob/master/kot... [3] https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.sync/-mutex/ https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-corout...
- PaulHoule 3y agoAlready you can uncheck exceptions by wrapping them in a function like the ones defined here https://paulhoule.github.io/pidove/apidocs/com/ontology2/pidove/util/DuctTape.html https://paulhoule.github.io/pidove/apidocs/com/ontology2/pid... that is something.stream().map(unchecked(SomeClass::methodThatThrows))
- Groxx 3y ago> 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 3y agoThis 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 3y ago> 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 3y agoIt is easy to put the right behavior in a function wrapper like https://github.com/paulhoule/pidove/blob/97f8ec697d2890f13ba1726fa3640d05e808fd6f/src/main/java/com/ontology2/pidove/util/DuctTape.java#L75 https://github.com/paulhoule/pidove/blob/97f8ec697d2890f13ba...
- dgellow 3y agoC# 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-references https://learn.microsoft.com/en-us/dotnet/csharp/nullable-ref...
- 3y ago
- bedobi 3y ago[flagged]
- codedokode 3y agoThis means you will have to write an if after every method call which is annoying and makes code less readable (source: had to write lots of ifs several days ago while writing a C library. My code consists mostly of ifs with error handling, because almost every function - even encoding conversion - can fail. I think that if companies hired low-paid trainees just for the purpose of writing ifs they could save a lot).
- mrkeen 3y agoIf it's repetition, you can abstract it away.
- mightyham 3y agoThere are very few cases where you can abstract away error handling because it's extremely context dependent.
- bedobi 3y agoI don't mean to be uncharitable but no, that's not how it works. You pass the Either<Error, Foo> or Maybe/Option<Foo> around, usually to the edge of the system where you map, flatMap or fold it into something.
- codedokode 3y agoBut this is ugly, for example, if I have a square root function, I don't want it to accept Error.
- bedobi 3y agoNo function should accept error
- agumonkey 3y agoNext: handling classes in functions.
- sgammon 3y agoI like it. `switch throws` looks like a really clean construct.
- mirekrusin 3y agoI 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 3y agoFrom 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 3y ago> 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 3y agoYou'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 ..."
- pizlonator 3y agoSuper 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.
- davidalayachew 3y agoExcellent. I love Checked Exceptions, and this just made them that more useful. Can't wait for it to land.