Remix.run Logo
OtomotO a day ago

Bah, no, I hated that you had to wrap basically every code block in a try/catch in Java, because the underlying lib could change and suddenly throw a Runtime-Exception.

At the same time Checked Exceptions were a nightmare as well, because suddenly they were part of the contract, even though maybe wrong later.

didntcheck a day ago | parent | next [-]

> the underlying lib could change and suddenly throw a Runtime-Exception.

And what would you do in that case? Since this is a future change your existing code presumably wouldn't know what else to do but throw its own exception, so why not just let that one propagate?

OtomotO a day ago | parent [-]

Always depends on which level it happens

quotemstr a day ago | parent | prev [-]

Checked exceptions are more trouble than they're worth. That doesn't make exceptions in general bad.

mathw a day ago | parent | next [-]

Not having checked exceptions is a huge problem, because then you never know when something might throw and what it might through, and in the .NET world the documentation on that is pretty awful and absolutely incomplete.

But then over in Java world, your checked exception paradise (which it of course isn't because the syntax and toolkit for managing the things is so clunky) is easily broken by the number of unchecked exceptions which could be thrown from anything at any time and break your code in unexpected and exciting ways, so not only do you have to deal with that system you also don't get any assurance that it's even worth doing.

But this doesn't actually mean checked exceptions are a bad idea, it means that Java didn't implement them very well (largely because it also has unchecked exceptions, and NullPointerException is unchecked because otherwise the burden of handling it would be hideous, but that comes down to reference types being nullable by default, which is a whole other barrel of pain they didn't have to do, and oh look, Go did the same thing wooo).

neonsunset a day ago | parent [-]

> in the .NET world the documentation on that is pretty awful and absolutely incomplete.

Depends on the area you look at. Language documentation is pretty good and so is documentation for the standard library itself. Documentation for the frameworks can be hit or miss. EF Core is pretty well documented and it’s easy to find what to look for usually. GUI frameworks are more of a learning curve however.

FWIW many in Java community consider checked exceptions to be a mistake. While I don’t find writing code that has many failure modes particularly fun with exception handling - Rust perfected the solution to this (and the Go way is visually abrasive, no thanks), I don’t think it’s particularly egregious either - Try pattern is pretty popular and idiomatic to use or implement, and business code often uses its own Result abstractions - switch expressions are pretty good at handling these. Personally, I’d write such code in F# instead which is a recent discovery I can’t believe so few know how good it is.

Cthulhu_ a day ago | parent | prev | next [-]

What does make exceptions bad in my opinion (and shared by Go developers?) is a few things:

1. Exceptions are expensive (at least in Java / C#), as they generate a stack trace every time. Which is fine for actually exceptional situations, the equivalent of `panic()` in Go, but:

2. Exceptions are thrown for situations that are not exceptional, e.g. files that don't exist, database rows that don't exist, etc. Those are simple business logic cases. The workaround is defensive coding, check if the file exists first, check if the row exists? that kind of thing.

3. The inconsistency between checked and unchecked exceptions.

4. Distance - but this is developer / implementation specific - between calling a function that can throw an error and handling it.

But #2 is the big one I think. Go's error handling is one solution, but if it's about correct code, then more functional languages that use the Either pattern or whatever it's called formally are even better. Go's approach is the more / most pragmatic of the options.

cesarb a day ago | parent [-]

> e.g. files that don't exist, database rows that don't exist, etc. [...] The workaround is defensive coding, check if the file exists first, check if the row exists?

Ugh NO. Please don't. You should never "check if the file exists first". It can stop existing between your check and your later attempt at opening the file (the same with database rows). That can even lead to security issues. The name for that kind of programming mistake, as a vulnerability class, is TOCTOU (time-of-check to time-of-use).

The correct way is always to try to do the operation in a single step, and handle the "does not exist" error return, be it a traditional error return (negative result with errno as ENOENT), a sum type (either the result or an error), or an exception.

OtomotO a day ago | parent [-]

Totally agreed, but as the previous poster wrote:

an exception is meant for EXCEPTIONAL behavior.

So it may be that the file access throws an exception but generally, I wouldn't agree.

OtomotO a day ago | parent | prev [-]

As said, I don't like the wrapping of about everything with try/catch

Sure, you can only do it way up the stack, but that's not enough quite often.

If you can only do it all the way up, I find it ergonomic.

Maybe I should experiment more with catch unwind in Rust.