@Ichoran then it really look like a solid solution for these specific use cases of error handling; it seems that checked exceptions can safely retire (or die? they are already retired).
Sorry to jump in this discussion, (maybe the other link discussing Result is a better place to ask) but, even though I totally agree with @Ichoran about Result (or Try or Either) being a very good way to deal with errors, could we watch this thread from the Java interop point of view?
Would it be possible to imagine a compiler feature that turns Java’s checked exceptions into this Result we’re talking about ? (and what would happen if the Java code can throw multiple exceptions?).
I’d be really interested in such a feature, when we think about it, Java’s checked exceptions are a first step into “encoding the error into the type system”. Far from being perfect, but I think having an automatic feature to turn everything we can “understand” from Java’s libraries (non-nullable, nullable, side-effects, etc.) would make interop so great.
Please let me know if this is completely out-of-topic, or simply bad, dillusional or whatever
Thank you.
EDIT: I don’t want my comment to sound as a “what if”. There’s certainly a good reason why checked exceptions cannot or should not be turned into such a Result type. Just a pointer to a good doc, a paper, or something else would be a nice answer, thank you.
If you want to catch just the checked exceptions, you need type unions, because you can get any one of the checked exceptions. So Scala 3 could possibly support this.
I’m just not sure it’s worth it. It’s quite a bit of compiler tooling and a bit of complexity in the language just to support a feature that overall isn’t as good as something that we already have.
I should point out that although I wrote my own .? macro and use it ubiquitously, this style of programming is not common in Scala. However, as a proof of concept that we have better-than-checked-exception capability in Scala, it works nicely. (And though it’s not common, basically all of my code uses it because it’s so effective at dealing with errors.)
Yes for sure, I could not tell if it’s worth the effort or not :\
It sounds “magical” (in the good sense of the term) and very useful to me.
A little back-story, in case it may help:
We’ve been working in Scala on some module that relies on an old Java module of ours. The old module dealt with data Validation through JSR-303 and added checked exceptions on top of the ValidationExceptions.
Obviously an Either-like system (or Valid/Invalid like in Vavr) would have been better here. But I can’t blame people who wrote this module ages ago to have, at least!, encoded possible errors in the Exception system. That’s all they had at this time, and to be fair, that’s really better than RuntimeExceptions, or some weird binary-encoded return code.
When we dealt with this dependency in the new Scala module, we were a bit disappointed to have to wrap every call to multiple methods within a Try. In fact, we started to feel really reliant on the compiler that helped us so much and suddenly that “compiler-as-a-security-nest” felt apart.
I’m definitely not the right person to argue in favor of such a feature, maybe it’s not worth the effort, maybe it’s not desirable at all, but hopefully the example we faced will be helpful.
As you said, when we first had a look at union types we thought about checked exceptions and it “felt like” some glue could have helped us automatically.
That was for the unbiased Either in Scala 2.11-, but it should be easily convertible to biased Either in Scala 2.12+ (I can do that if you want).
The machinery also could probably be simplified, e.g. Either[Either[Error, Result], Result] could be changed to EarlyEither[Error, Result] that has methods like def flatMap[B](A => Either[E, B]) (to allow composition with ordinary Eithers) and def extract: Either[E, A] (to designate early return boundary).
Keeping up such a fiction in the face of overriding and implementing Java methods sounds really hard though. And it would require Scala to be way more opinionated about how a developer is supposed to do error handling.
1/ (It’s opinionated and should not be a first-class-citizen): absolutely. That could be something we could try to implement, test, and propose afterwards? Is it even possible? Any helpful pointer (macros should be the thing to look at?) at some implementation / module / compiler plugin / … that would do something similar we could look at to get started would help a lot.
2/ (It’s hard) : I’m trusting you on this. So we have to try-and-tell (thus => question 1).
We’d be happy to try and contribute something like that, if someone has pointers to share, it’d be really awesome.
Thanks a lot.
EDIT: (to avoid the noise of another message). Thanks @LPTK for pointing all of the issues. And sorry for the inconvenience.
Then I could call test with a Java object o whose foo method throws, which according to the Scala compiler fiction would mean returning Either. So Scala will tell you test(o) has type Either[...] when in reality it just throws, and there is no specific place for the compiler to insert the exception-to-either-conversion logic.
Another example: when implementing, in Scala, a Java class with an abstract method that throws, what should the compiler do? It would have to silently make the Scala implementation actually throw (to comply with the Java method’s runtime semantics and erasure). Then it would have to somehow mark this Scala method as Java-throwing under the scenes, so callers deal with it accordingly. But all this would make the Scala method actually incompatible with a Scala method that properly returns an Either, so you couldn’t at the same time implement a Scala interface with it, although the Scala types would seem to match.
As compiler fictions go, this one would be on the difficult-and-leaky side.
Hey, can I revive this discussion a bit? I skimmed over it and I personally think it got side-tracked into a Either/Result-oriented design discussion. Let’s bring it back to specifically checked exceptions and maybe try to simplify a bit.
My suggestion is this: have an analysis tool which checks Scala code for use of the throw keyword without a corresponding try ... catch which catches that exception in the same method. If it finds such usage, log a warning. If the method that uses throw is annotated with a @throws[...] annotation for the same exception type that’s being thrown, then don’t log a warning. This way exception warnings are propagated (or not) throughout the call graph in the same way as exceptions are.
The advantages of using the existing @throws annotation:
No new keywords needed
Codebases which use the annotation already will automatically work with this checker
The checker itself can be implemented in a few different places: as a Scala compiler warning, or perhaps an sbt plugin, or maybe even just a completely separate tool. In fact, reanalyze could maybe even be ported over to analyze Scala, since someone has written a Scala parser for OCaml: pfff/Parser_scala_recursive_descent.ml at develop · returntocorp/pfff · GitHub
I think not substantially different. To me it seems like the reasons why people hate Java checked exceptions–being forced to deal with them in every method, and lack of composeability–are either considered virtues or mitigated by certain patterns in Rust-style Result<T,E> error handling, so perhaps what people really hated was the fact that it was baked in and could never be turned off? In which case it would probably make sense to implement it as either a separate tool, or as a compiler warning, say -Ywarn-exceptions.
Yes, I consider that a form of Rust-style (Result<T,E>) error handling. I would like to, as I said earlier, bring this discussion back to specifically about checked exceptions.