Exception based error handling is so bad and unsafe that adopting functional error handling with Either, Try etc as implemented by functional addon libraries for many languages, while not yet common, in time it will become the new default even in OO languages. (just like it's been the default in functional languages for decades)
Functional error handling types are much simpler, safer and more powerful.
Simpler because they don't rely on dedicated syntax- they're just regular objects no different to any other object.
Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. (no risk of ignoring errors and no risk of catching a higher level of error than desired, ubiquitous bugs in exception based error handling)
Powerful because they support map, flatmap, applicative etc, making it easy to eg chain multiple computations together in desired ways, which is unwieldy and bug prone when using exceptions.
> What is wrong about dedicated syntax
It adds complexity to the language! It could be that, when learning Java, Kotlin and any other language, we learn that methods return what they say they do... and that's that. No weird dedicated syntax and magic, special treatment for returning anything other than the happy path, and the HUGE complexity that comes with it, eg the dedicated syntax itself and how it behaves, differences between checked and unchecked exceptions, hierarchies of exceptions etc etc.
> Exceptions are easier
That's the point, they're not. Exceptions based error handling is unnecessary, hugely complex, doesn't compose at all, obfuscates or straight up hides what can go wrong with any given call, so leads to countless trivially preventable bugs... I could go on. And after decades of use, there's still no consensus about what exceptions should be or how they should be used. Exceptions are a failed experiment and I have no doubt that in ten years, Java, Kotlin and many other languages will acknowledge as much and move away from it the same way Joda Time outcompeted and replaced the horrible Java date and time library.
> unlike exceptions, they force callers to handle all potential outcomes, but no more.
Checked exceptions in java also have that property.
> they support map, flatmap, applicative etc, making it easy to eg chain multiple computations together in desired ways, which is unwieldy and bug prone when using exceptions.
Isn't that exactly the kind of thing this proposal aims to improve upon?
> What is wrong about dedicated syntax [...] It adds complexity to the language!
It sounds like you're basically arguing that the less syntax a language supports, the easier it is to use. But poll some beginner programmers about which language they find easier to use between Lisp and Python and I'm certain they'll say Python.
Syntax has a purpose and if you take all the complexity out of the syntax then you'll just be pushing that complexity into the standard library instead. That's not inherently better, and personally I think it makes more sense to distribute that complexity across the various tools involved. That way, the syntax of the language can align better with the way we read and interpret natural language, and make it easier to process some of that complexity.
> Checked exceptions in java also have that property
No they don't. How many times have you seen code like
try { //whatever } catch (Exception e) { //now we're catching ALL exceptions, yay }
or, worse
try { //whatever } catch (Error e) { //lol }
re syntax, I guess some folks like more syntax but the point is if the syntax is being added to solve a problem that's solved in a way that's strictly objectively better in another way, it's pointless (and we're not talking about just complex syntax and all the foot guns and complexity that comes with hierarchies with exceptions, differences between checked and unchecked etc etc, we're talking about actual execution - try catch is at the end of the day a JVM hack that slows down execution and does something weird which could be easily avoided by just returning plain objects
And in your scenario where you handle errors with an Either type, how many times have you seen code where that kind of thing just gets upcasted because the caller can't be bothered to deal with the specialized error? I think this is a strawman argument against exceptions and it's equally possible to do it wrong with your way as it is with my way.
> Exception based error handling is so bad and unsafe that adopting functional error handling with Either, Try etc as implemented by functional addon libraries for many languages, while not yet common, in time it will become the new default even in OO languages. (just like it's been the default in functional languages for decades)
I honestly doubt this. Especially since Java is investing further into exceptions with this JEP.
> Functional error handling types are much simpler, safer and more powerful. Simpler because they don't rely on dedicated syntax- they're just regular objects no different to any other object.
Who cares? Languages can build the syntax, make it easier, and make it consistent rather than having specific dialects that comes from libraries. Either, Try, Result which one do I choose? This is better delegated to the language rather than DSLs.
> Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. (no risk of ignoring errors and no risk of catching a higher level of error than desired, ubiquitous bugs in exception based error handling)
> Safer because unlike exceptions, they force callers to handle all potential outcomes, but no more. (no risk of ignoring errors and no risk of catching a higher level of error than desired, ubiquitous bugs in exception based error handling)
I'm not sure they are safer at all. Either, Try, and Result don't force you to check the errors, they all offer escape hatches. How many times has there been rust code that just calls `.unwrap` because the author doesn't care or thinks the error can't occur. I've seen tons of Kotlin code that uses !! to get out of null checking because the author doesn't believe something could be null and can't handle it actually being null.
I'm with you right up until the dedicated syntax point.
Failure modes aren't going away, (especially with our tendency towards slinging JSON at each other as fast as possible.)
So, make a good syntax for it! Then accounting for failure becomes pleasurable, so developers will do it. Take the hit and make your user base learn it once, early.
But perhaps I misunderstood what 'dedicated syntax' is. I want syntax for map/flatMap because it's really broadly useful. But I don't like it when one particular implementation gets special syntax, and the rest don't (looking at you, Rust-Result, and null-coalescing operators).
> Exceptions are a failed experiment
They still need to exist in some form. We're not going to case-match over every plus and divide to deal with overflow and div0.
"Exceptions should only be used exceptionally" is wisdom that never really stood a chance, because mainstream languages didn't provide a way to handle non-exceptional errors.
> HotSpot will do things like recompile methods on the fly to disable stack trace generation, if profiling shows that your code is throwing and catching exceptions way more often than expected
I was under the impression that to get good performance here you had to override getStackTrace.
I've seen codebases using Either and other monad constructions that worked fine on the happy path but had nowhere near adequate error handling, I think they thought because they used Either they didn't need to think about error handling.
This kind of thing tends to disappear in my Java day job:
User user = getUser(userId);
What's wrong with the above? Maybe nothing, maybe everything. I don't know! I need to start ctrl-clicking into definitions to try to hunt down the failure modes.
Note catching exceptions too close to where they are thrown is as much of a risk as catching them too far away.
I have dealt with so much Scala code that is just too clever. Like the kind that tries to parallelize a trivial task but has race conditions anyway and doesn't use all the CPUs. You can spend two days trying to fix it or you can finish the job in 20 minutes with the ExecutorService.
And to avoid having any code ever fail. It was a formative experience to me to type in the source of a terminal emulator in C for CP/M that I ported to Microware’s OS-9 out of a 1984 Byte magazine that tried to do things the Either way without language support and noted about 4/5 of the code was error handling interspersed with the happy path. Either is not much better than errno in the end.