[HN Gopher] Why checked exceptions failed
       ___________________________________________________________________
        
       Why checked exceptions failed
        
       Author : rsaarelm
       Score  : 83 points
       Date   : 2023-07-15 12:30 UTC (10 hours ago)
        
 (HTM) web link (borretti.me)
 (TXT) w3m dump (borretti.me)
        
       | javajosh wrote:
       | Checked exceptions are used to make up for Java's inability to
       | return more than one value, plus it's inability to wrap two
       | values without defining a new type. In other words, I think
       | checked exceptions are basically a symptom of a lack of object
       | literal syntax. This is in addition to their status as a "cool
       | language feature" that is a siren song to new, bright programmers
       | looking to spice up their designs. Exceptions are in general
       | problematic (they move the program counter in a disjoint way -
       | "non-local change of control" I think it's called) so it makes
       | this particular siren song doubly deadly.
       | 
       | It would probably be handy if someone wrote a pamphlet on
       | "Refactoring Exceptions" to give teams the confidence to refactor
       | exceptions out of their code. I'll offer $20 to Martin Fowler to
       | write such a thing.
        
         | dudul wrote:
         | Just came to say this. I think the best way to represent
         | failure is in the return type, but Java made that very
         | cumbersome and noisy.
        
         | kaba0 wrote:
         | Exceptions "don't move the program counter in disjoint ways",
         | they are part of the "structured gotos". In fact, it has the
         | same control flow as an early return does, with the handler
         | being _locally_ found in a parent's (recursively) method body.
         | 
         | Also, the point about multiple return types is pointless -- it
         | already has Optional, a proper Return type is completely
         | feasible to implement and use in Java. So is a Pair<A,B> if
         | tuples are what you mean. Some are just not part of the
         | standard lib by default.
        
           | Supermancho wrote:
           | > Return type is completely feasible to implement
           | 
           | Yet it is rarely done. Lots of boilerplate to replace what is
           | easily done in other languages. If java provided a native
           | tuple type, new patterns would appeat. As it is, too many
           | lines for 1 off returns. Send off a serialzed json and
           | deserialize it later. Easier than specific dtos.
        
           | javajosh wrote:
           | _> with the handler being locally found in a parent's
           | (recursively) method body_
           | 
           | Yeah, that doesn't sound disjoint /s
        
             | kaba0 wrote:
             | That's the exact same thing as if you had a function call
             | as the last statement in a functional call as a last
             | statement, etc. Literally just popping off stackframes.
        
               | javajosh wrote:
               | Popping 1 stack is a normal return; popping N stacks is
               | precisely what is meant by "no-local (or disjoint)
               | transfer of control".
        
               | kaba0 wrote:
               | It pops one stack at a time only. It's up to that
               | function call to determine how to proceed further, the
               | exact same way how it would happen with a last-call-chain
               | I was talking about.
        
           | throw3823423 wrote:
           | But the issue about the multiple return types is not
           | pointless at all: Optional types, and pair types, were added
           | to the language way later than checked exceptions. We didn't
           | even have actual generics back then! So the checked
           | exceptions really were a way to get around lacking
           | alternative error management features.
           | 
           | If backwards compatibility wasn't a concern, checked
           | exceptions would probably go away in a version or two, and
           | we'd have some kind of monadic error type instead. But Java
           | takes this seriously, and the standard library itself has
           | methods with checked exceptions, so the timeline to go from
           | checked exceptions to something else is very long.
        
         | spreiti wrote:
         | FYI: With Java 21 (2023/09/19) this is finally possible.
         | 
         | Relevant JEPs:
         | 
         | - 441: Pattern Matching for switch
         | 
         | - 440: Record Patterns
         | 
         | - 409: Sealed Classes
         | 
         | - 395: Records
         | 
         | It is kind of similar to Scalas version of pattern matching
         | with case classes.
        
           | isbvhodnvemrwvn wrote:
           | And it won't get adopted any time soon due to backwards
           | compatibility and existing code. Anybody who wanted to use it
           | used vavr at this point.
        
       | TrianguloY wrote:
       | As others have mentioned, an interface is a contract and any
       | possible exception that may be thrown needs to be explicitly
       | defined, that's why checked exceptions exists.
       | 
       | However, there is an exception (pun intended) to this: inline
       | lambdas. You can use lambdas as variables, pass them as
       | parameters, etc. And it makes sense for them to retain the
       | interface if needed, but (at least on java) when you are doing a
       | .map(v->parseInt(v)) checked exceptions can't escape the lambda,
       | and that's probably the worst problem of java lambdas.
       | 
       | You can avoid this with custom interfaces where the exception is
       | a generic parameter, but sadly native ones aren't defined like
       | this (yes, exceptions that a function throws can also be defined
       | with generics, and the compiler will automatically use the more
       | restrictive of the lambda code! )
        
       | tetha wrote:
       | Yeah. The thing is, I like the idea of checked exceptions. You
       | could go ahead and define a number of error situations or error
       | kinds and annotate methods with these checked exceptions to force
       | applications to handle these errors. Like, you have a
       | Database.query function and it might throw different exceptions -
       | ConnectionInterrupted, QueryPreparationFailed, QueryFailed,
       | TransactionCancelled. And then you could catch specific error
       | kinds and react differently on those - retry for interrupted
       | connections, cancel on query errors.
       | 
       | This however runs into issues. One really annoying one: Checked
       | exceptions are part of your API. If you forget an error condition
       | and want to introduce a new exception for that, well guess what,
       | that's a breaking API change. There is no easy way to evolve an
       | API with checked exceptions, because changing them requires new
       | major versions, because it prevents code from compiling.
       | 
       | On top, if you want to encapsulate your API properly, you end up
       | with a lot of boilerplate. Once you're using a library, and that
       | library has checked exceptions, you either have to base your own
       | public API on the API of that library - meaning you can never
       | replace it - or you have to start wrapping all manner of errors
       | into your own checked exceptions. I've kinda done it as an
       | experiment some time ago, but that just ends up with so many
       | exception types and so much error wrapping it's kinda ridiculous.
       | 
       | And then you end up with the sad truth on top to be honest: Most
       | error checking is rather brute and clumsy. In most cases, I just
       | let exceptions bubble up because my intermediate function can't
       | really do anything about it. As a distant second, you catch all
       | errors, shove them in some kind of error reporting, reset the
       | system and continue trucking. As a somewhat similar third, I
       | dissect errors in CLI tools to create some useful error messages.
       | And only them I might start caring about some specific errors,
       | but that's pretty rare if you're just running some REST-based
       | business logic.
       | 
       | All in all, it's a good idea, but the implementation results in a
       | lot of API churn or boilerplate for something that's not used
       | much in general.
        
         | patrick451 wrote:
         | > This however runs into issues. One really annoying one:
         | Checked exceptions are part of your API. If you forget an error
         | condition and want to introduce a new exception for that, well
         | guess what, that's a breaking API change. There is no easy way
         | to evolve an API with checked exceptions, because changing them
         | requires new major versions, because it prevents code from
         | compiling.
         | 
         | This sounds like a good thing. Throwing a new exception _is_ a
         | breaking change whether it 's enforced by the compiler or not.
         | Otherwise, callers of you API go from being exception safe to
         | not being exception safe. It's strange to me that you would
         | want to squirm around that just to avoid bumping the version
         | number.
        
           | tetha wrote:
           | That's not true in java.
           | 
           | You can gradually increase the granularity of unchecked
           | exceptions using subtypes. For example I might initally have
           | AnythingFailedDuringTheQueryException with a Subtype of
           | QueryPreparationException. If I introduce new subclasses of
           | QueryPreparationException to increase the precision of the
           | error reporting, all existing catches for
           | QueryPreparationException will still function as they did
           | before. A "NotEnoughParametersException" subclass of a
           | QueryPreparationError is still caught as a
           | QueryPreparationError. Only if you introduce catches for the
           | new more granular unchecked exception, your codes behavior
           | changes to use the new behavior.
           | 
           | That's a very clean way to improve error reporting in a
           | backwards compatible way.
        
       | grumbel wrote:
       | Little aside, but I feel a lot of the drama surrounding exception
       | could have been solved with a little syntactic sugar making their
       | handling easier. Something along the lines of Perl's "||
       | die("...")" pattern would be a start (i.e. add some context and
       | rethrow).
       | 
       | In C++ I find it quite infuriating that try{} opens up a new
       | lexical scope, which means you can't construct something, check
       | for errors and move on, since the act of adding a try{} means
       | you'd call the destructor of the thing you just constructed.
       | Meanwhile surrounding a whole block of code with a try{} means
       | you no longer can tell where the exception came from.
        
         | senkora wrote:
         | > In C++ I find it quite infuriating that try{} opens up a new
         | lexical scope
         | 
         | That's an interesting point. I've never considered that because
         | I haven't worked with classes that regularly throw in their
         | constructors.
         | 
         | What situation do you have where 1) you are constructing
         | classes that regularly throw in their constructors and 2) this
         | is a recoverable error that 3) should be handled at the point
         | where the class is constructed instead of some outer point?
        
           | grumbel wrote:
           | Code like:                 QImage image("filename.png");
           | 
           | is pretty common. Qt solves this by not throwing an exception
           | and using a Null image check:                 bool
           | QImage::isNull() const
           | 
           | but that throws all the extra information that an Exception
           | could provide away.
        
         | specialist wrote:
         | > _infuriating that try{} opens up a new lexical scope_
         | 
         | This _slays_ me.
         | 
         | The try, catch, and finally blocks should be just one lexical
         | scope.
        
           | xen0 wrote:
           | I don't think that helps?
           | 
           | If, as is kind of the point of RAII, your constructors do
           | some initialization and that initialization may fail, then:
           | 
           | If you have a single object that may fail to construct then
           | maintaining it in scope for the catch block doesn't make
           | sense; it's not initialized.
           | 
           | If you have several, then you still can't maintain them in
           | scope, because you don't know which ones are actually valid.
           | 
           | Yes there are other patterns that wouldn't have this issue
           | (acquiring resources outside of the constructor), but then
           | you can just declare your objects outside of the try-catch
           | block.
        
             | specialist wrote:
             | I misspoke.
             | 
             | Ignoring the syntax for a moment, I want scoping to behave
             | like this pseudo-code. So that the catch and finally blocks
             | are lexically nested within the parent try block.
             | try {              OutputStream out = ...              ...
             | // Must be at end of try block       catch ExceptionA,
             | Exception B { ... }            catch Exception C { ... }
             | finally { ... }                     } // end of try
             | 
             | Maybe even allow catch and finally to allow single
             | expression in addition to blocks. Just like with
             | if/then/else.
        
               | xen0 wrote:
               | Right, but if constructing OutputStream throws
               | ExceptionA, you shouldn't have it in scope; it's not
               | something you can use.
        
               | nemetroid wrote:
               | If constructing "out" fails but one of the catch-blocks
               | tries to use "out", what should happen?
        
               | grumbel wrote:
               | Python is an example of a language that works like this.
               | When the constructor throws, 'out' remains not defined,
               | but you can just do 'out = ...' in the catch block and
               | define one with a fallback value. So all the code after
               | the error handing would just see a working 'out'
               | variable. This works due to all variables being bound to
               | the function scope, not to the code block, in Python.
        
               | nemetroid wrote:
               | In Python, sure, but I struggle to see a way to make it
               | make sense in C++.
        
         | mostlylurks wrote:
         | > In C++ I find it quite infuriating that try{} opens up a new
         | lexical scope, which means you can't construct something, check
         | for errors and move on
         | 
         | You technically can with a small workaround (demonstrated
         | below), though I personally wouldn't use this approach.
         | void foo() {           auto value = [] {             try {
         | return Foo();             } catch (...) {               //
         | Something here             }           }();
         | value.do_something();         }
        
           | grumbel wrote:
           | Yep, I had to resort to that a few times. With a bit of
           | syntactic sugar that could be turned into something like:
           | auto value = try Foo("will fail")          catch(...) {
           | return Foo("will work"); // or throw something else
           | };
        
       | ndufjfhfufjdufj wrote:
       | [flagged]
        
       | cvoss wrote:
       | > Can I specify that implementations of foo can throw no, some,
       | or all exceptions? What would it even mean to write something
       | like throws *? Analogously, if I have a function that takes a
       | method as an argument, like a callback, how do I specify what set
       | of exceptions in can throw? Can I have generic "exception set
       | parameters"?
       | 
       | > Concretely, checked exceptions in Java failed because Java
       | lacks "throwingness polymorphism", if you will.
       | 
       | I don't understand. _Every_ "missing feature" the author asks for
       | is, in fact, present in Java. The example below just about sums
       | it up.                 @FunctionalInterface       interface
       | MyCallback<X, Y, E extends Exception> {              Y apply (X
       | x) throws E;                }              class A {
       | <X, Y, E extends Exception> Y runCallback (MyCallback<X, Y, E>
       | func, X x) throws E {           return func.apply(x);         }
       | }
        
         | tadfisher wrote:
         | The author's point about the orthogonality of exceptions to
         | values and types still stands; try creating a class like
         | abstract class GenericException<R> extends Exception {
         | R reason();         }
         | 
         | As it is, Exception is a "special" type that isn't allowed to
         | be used like normal Java types, because Java generics are a
         | leaky abstraction introduced after checked exceptions, and the
         | JVM cannot deal with type erasure in catch blocks.
        
       | invalidname wrote:
       | My opinion is the exact opposite see
       | https://debugagent.com/everything-bad-in-java-is-good-for-yo...
       | 
       | Checked exceptions are unpopular since no one likes
       | responsibility. But they are great when used right. Calls that
       | must have proper cleanup after them e.g. SQL, IO are checked. The
       | fact that this must be communicated via interfaces is hugely
       | important.
       | 
       | There are "weird" problems such as stream close() throwing a
       | checked exception. That's an API misbehavior that has a
       | workaround thanks to try-with-resources.
        
         | crabbone wrote:
         | This comes from a misunderstanding of the _reason_ why
         | exceptions where designed they way they were. The whole point
         | of exceptions bubbling up w /o having to write support code to
         | deal with passing exceptions further is to make it so that the
         | purpose of the function is clear to the reader.
         | 
         | Go's exceptions have the same unfortunate property as Java's
         | checked exception. And that's what makes Go's code atrocious.
         | Every other line you see something like:                   if
         | x, e := f(); e != nil {             ...         } else {
         | return y, e         }
         | 
         | It makes it very easy to make mistakes when you have to write a
         | lot of repetitive code. You make typos, and because they often
         | land on the "bad" path, they aren't immediately discovered. You
         | have to memorize the state of your function wrt' variable
         | initialization, because now you cannot automatically initialize
         | and destroy them all together. You need to create a lot of
         | helper variables whose purpose is only to transfer return value
         | from one function to another...
         | 
         | If you think that you want checked exceptions, then you don't
         | want exceptions at all. You are denying them the very purpose
         | they were created for. But there are alternative ways to deal
         | with unexpected events in program execution. Monads would be
         | one of those. So... maybe just don't use exceptions?
        
           | dimgl wrote:
           | > Go's exceptions have the same unfortunate property as
           | Java's checked exception.
           | 
           | Wait what? Here's where you lost me. You don't "throw" errors
           | in Golang. Instead it's common practice for functions to
           | return multiple values, one of which can be an error. Errors
           | are just structs that implement the Error interface.
           | 
           | The language (and the compiler by extension) doesn't make you
           | explicitly handle errors. On the contrary: you can choose to
           | ignore errors altogether.
           | 
           | On the other hand, a checked exception in Java is one that
           | MUST be either caught or declared in the method in which it
           | is thrown. Code that fails to do this won't compile. You're
           | completely wrong here.
        
             | tsimionescu wrote:
             | The point GP was making is that both Go and Java (in code
             | which makes heavy use of checked exceptions and different
             | exception types) mix together the business logic with error
             | propagation. So, even though in Java you throw exceptions
             | and in Go you return error values, they both end up (or
             | _can_ end up, in the case of Java) having lots of error
             | handling boilerplate all around a function.
             | 
             | In Java of course this doesn't _have_ to happen, but it can
             | end up happening if you chose to have many exception types
             | and different exceptions types when crossing layers (e.g.
             | try { doX(); } catch (IOException e) { throw new
             | MiddleLayerException( "failed to do X", e);}).
             | 
             | To be fair though, Java still allows you to write that like
             | this:                 try{          x = doX();         y =
             | doY(x);         z = doZ(y);       } catch (XException |
             | YException | ZException e) {         throw new
             | MiddleLayerException("...", e);       }
             | 
             | Which is still better than Go's:                 x, err :=
             | doX();       if err != nil {         return
             | MiddleLayerErr(err);       }       y, err := doY();
             | if err != nil {         return MiddleLayerErr(err);       }
             | z, err := doZ();       if err != nil {          return
             | MiddleLayerErr(err);       }
        
           | invalidname wrote:
           | No. This comes from a misunderstanding of checked exceptions.
           | 
           | Checked exceptions don't mean I need to handle the exception
           | right now. They mean I need to either do that or declare
           | throws. Declaring throws is fine it implicitly documents the
           | code and enforces a similar requirement up the chain.
           | 
           | Checked exceptions aren't the default and shouldn't be.
        
             | naasking wrote:
             | Checked exceptions should be the default, exceptions should
             | just be rare. They are overused in almost all languages.
        
               | invalidname wrote:
               | What about: NullPointerException?
               | ArrayIndexOutOfBoundsException? IllegalArgumentException?
               | UnsupportedEncodingException?
        
               | naasking wrote:
               | What about them? If your language semantics are pervaded
               | by exceptions, that kinda suggests your language sucks.
               | If you're unlucky enough to find yourself in this
               | situation, then make exception contracts simple to
               | express and propagate.
        
               | bsder wrote:
               | One of these things is not like the others ...
               | 
               | The first 3 need to terminate your thread because
               | something is irretrievably incorrect. The only sane thing
               | to do is stop.
               | 
               | "UnsupportedEncodingException" has _lots_ of things you
               | can do to recover. You can try different encodings. You
               | can request retry of someting. You can check a CRC for
               | corruption. etc.
               | 
               | So, the first 3 really shouldn't litter your code. The
               | final one probably should.
        
               | MrJohz wrote:
               | Null pointer exceptions shouldn't exist in the first
               | place, they're fairly trivial to avoid. Index out of
               | bounds can be similarly avoided, although the cost in
               | terms of economics is a lot steeper. I like Rust's
               | strategy of providing a checked and an unchecked indexing
               | mechanism, where the unchecked indexing mechanism
               | typically crashes the program rather than just throwing
               | an exception.
               | 
               | Illegal arguments can often be avoided in the first place
               | with sensible types (although, like null pointer
               | exceptions, this typically requires some language
               | support), and unsupported encodings should be rare
               | assuming widespread UTF-8.
               | 
               | So yes, I agree, those should all be very rare exceptions
               | to find in a function signature.
        
               | matwood wrote:
               | Agreed. Too often exceptions end up driving normal
               | control flow, when the should just be...exceptions.
        
               | ajanuary wrote:
               | You must have a real panic when you see a sign saying "No
               | parking except weekends." Weekends happen all the time!
        
               | naasking wrote:
               | You're right, weekends happen all the time, which means
               | they're not exceptional, so you shouldn't throw an
               | exception if you encounter a weekend.
        
               | theshrike79 wrote:
               | So if you write code that handles dates you'd throw
               | NotWeekendException to notify the caller that it's not
               | weekend?
               | 
               | Exceptions should be for exceptions, not control flow.
        
               | tremon wrote:
               | What do you mean when you say "exceptions should be
               | rare"? Do you mean they should not occur frequently
               | during runtime, or that it should be rare to see explicit
               | exception handling in a piece of code?
        
               | [deleted]
        
               | naasking wrote:
               | Situations in which you must raise, propagate and handle
               | exceptions should be rare.
        
               | amalcon wrote:
               | The fundamental thing an exception does is allow you to
               | choose where in the stack to react to the exception. The
               | fundamental thing a checked error does is force you to
               | actually choose (even if that choice is to bubble all the
               | way up and crash the program), rather than forget and end
               | up with a bug.
               | 
               | The choice about where to handle the exception is almost
               | always a good thing, since it reduces boilerplate in
               | dealing with that condition. The cases where it's not a
               | good thing are when it's either impossible to deal with
               | the condition at all (e.g. assertion failure) or where it
               | must obviously be handled locally (e.g. errno==EAGAIN
               | type stuff).
               | 
               | The requirement to actually make the decision is a good
               | thing in some cases (e.g. I/O), but not in other cases
               | (e.g. out of memory) because the overhead of making this
               | decision so often is larger than the benefit of avoiding
               | that class of bug. It's beneficial when it's something
               | that can only happen while doing certain well defined
               | activities.
               | 
               | There are also places where there just shouldn't be an
               | error at all. E.g. null pointers shouldn't be an error;
               | your program should be rejected if the compiler can't
               | prove that this is impossible through static analysis
               | (even if that just forces the author to write
               | assert(ptr!=0)).
               | 
               | I guess I'm converging on a scheme that looks like: I/O
               | errors are checked exceptions. Out of memory is an
               | unchecked exception. Assertion failures are panics which
               | cannot be caught. Division by zero is prevented by static
               | analysis. I'm not sure what should be done with
               | EAGAIN/EWOULDBLOCK type stuff -- it doesn't really feel
               | like those should be exceptions, but introducing a whole
               | new "error" handling idiom just for this doesn't feel
               | great either.
        
             | astral303 wrote:
             | Declaring throws end up leaking layers of abstractions in
             | practice. Many thrown checked exceptions are not
             | appropriate to pass above certain layers. It is often not
             | OK to expose checked exceptions that are really
             | implementation details up the chain---it's not a "similar
             | requirement"---it is exactly the same requirement and often
             | an inappropriate one at many levels up the chain.
             | 
             | If you just pass exceptions up in throws, you end up
             | causing cascading checked exceptions chain changes and
             | creating a bunch of noise, which is what many times people
             | resort to doing in practice, because they don't have the
             | experience and it is very rare/almost never done to have
             | any kind of automatic enforcement/linters/static analysis
             | that prohibits implementation detail checked exceptions
             | from propagating past above certain levels.
             | 
             | It is almost never OK to propagate IOException, for
             | example, past maybe 1 or 2 levels of private helper
             | methods, almost never should be propagated past your class
             | boundary and you have to think twice about adding throws to
             | protected methods even, unless your subclass
             | implementations are specifically implementing alternate IO
             | calls.
             | 
             | So yes that's why checked exceptions have failed. And when
             | you actually take this extreme discipline to your code, yes
             | you end up with a ton of try/catch handling noise and a
             | whole lotta new exceptions classes, and it takes similar
             | extreme discipline to always avoid poor/improper try/catch
             | handlers. Refactoring code across certain boundaries
             | becomes a much bigger hassle, so your end up discouraging
             | refactoring across those boundaries, which leads to code
             | that is more easily stale and design decisions that are
             | much harder to back out of.
        
         | throwawaymaths wrote:
         | Not exceptions, but zig has explicit error return types, and it
         | works great. Especially since you can _also_ just  "tell the
         | compiler to figure it out". There are a few cases where the
         | compiler can't figure it out, but in those cases you grudgingly
         | annotate the error types and it's done.
        
         | specialist wrote:
         | From your OC:
         | 
         | > _Null is fast. Super fast. Literally free._
         | 
         | Yes and: The JIT will optimize away Null Object method
         | invocations.
         | 
         | All my composable classes have a Null Object. No null checks
         | means concise iteration (eg graph traversal). Eliminates NPEs.
         | Just as fast.
         | 
         | Win / win / win.
         | 
         | Optionals (classes and operators) continue to be turrible
         | mistakes. So much unnecessary effort, so little benefit.
         | 
         | Ditto @Nullable and @Nonnull.
         | 
         | Someday, maybe, I'll make a javac compiler plugin which
         | autogenerates Null Object implementations and converts "Node
         | abc = null" to "Node abc = Node.DEFAULT_NULL_OBJECT_INSTANCE".
         | 
         | https://en.wikipedia.org/wiki/Null_object_pattern
        
           | anonymoushn wrote:
           | It seems like when one is implementing Null Objects and
           | making incomplete attempts to prevent values from ever being
           | null it is not really an argument in favor of nullable values
           | being good and the only type of values that should be present
           | in a language.
        
             | specialist wrote:
             | I'm not smart enough to parse this. Try again?
        
               | anonymoushn wrote:
               | "All non-primitive values are nullable references" is a
               | feature of the language. You posted that you are trying
               | to avoid using that feature (instead using non-null
               | references to a special Null value?) and trying to avoid
               | having null references for the types you create. It seems
               | like you do not actually think the feature is a good
               | feature.
        
               | specialist wrote:
               | > _Ditto @Nullable and @Nonnull._
               | 
               | I was referring to the annotations. My proposal moots
               | them.
               | 
               | References would still be nullable.
        
         | scarface_74 wrote:
         | > Calls that must have proper cleanup after them e.g. SQL, IO
         | are checked.
         | 
         | Why? In C# you use a "using" block and whether your code exits
         | the using block successfully or with an exception, the cleanup
         | is automatic.
         | 
         | RAII has been around at least since the 80s with C++.
        
           | invalidname wrote:
           | We have it in Java too. For us it's try-with-resources and
           | has nothing to do with checked exceptions. Checked exceptions
           | remind you that you need to wrap the code and need to take
           | that into account.
           | 
           | They also force you to declare that an exception is thrown if
           | you don't want to handle it in the current method. That's
           | important as the signature of the method carries an important
           | failure that can't be dismissed and is enforced by the
           | compiler.
        
             | scarface_74 wrote:
             | What "reminder" do you need to wrap your code? I can't
             | think of an instance that you wouldn't wrap your code in a
             | try/catch block around your code and either log and crash,
             | log and continue, or for a specific exception that you know
             | about, do "something". But most of the time it's the first
             | two if you're using RAII.
        
               | invalidname wrote:
               | Let's say I write an API for handling IO. DB or File or
               | networking or anything really.
               | 
               | I get a failure, what do I do?
               | 
               | I can throw an exception, but this is important how do I
               | know the user of my API will actually do the cleanup
               | after me. I can't do the cleanup since this is a part of
               | an API not the actual usage of the API. How can I give
               | the user of this API the right set of hint that "you need
               | to pay attention to this" without worrying too much.
               | 
               | Checked exceptions are that. A large part of the problem
               | is when people use them for things that aren't important,
               | but when they are important I can just declare a "throws"
               | and know that someone will handle the exception along the
               | chain.
               | 
               | With runtime exceptions I have no guarantee.
        
               | funnymony wrote:
               | Any chance you have a code/pseudo code example of this?
               | 
               | My naive take on this would be "just use raii/closable".
               | (But easily and likely, I misunderstood)
        
               | invalidname wrote:
               | Let's say Java had no checked exceptions. I could just
               | write IO code without doing a try since no one is forcing
               | me to check anything.
               | 
               | But let's go further. Let's say I have an API x(). This
               | API accesses my database as part of a larger transaction.
               | 
               | I invoke x() and it fails with an SQLException which is
               | declared. I can revert the transaction or I can choose to
               | retry x(). The checked exception notifies me that there's
               | an important decision I need to make at this point,
               | cleanup is one option but in some cases there are more.
               | Furthermore, cleanup isn't always enough. In a case of an
               | IOException I'd often want to notify the user e.g. if the
               | disk is out of space I want to show an error message
               | somewhere...
        
               | scarface_74 wrote:
               | > Let's say Java had no checked exceptions. I could just
               | write IO code without doing a try since no one is forcing
               | me to check anything.
               | 
               | Any decent C# linter will notice that the class exposes
               | (IDisposable) and warn you that you should at least be
               | wrapping it in a "using" block.
        
               | invalidname wrote:
               | That's true for Java too. But that's only a part of the
               | problem which is why I gave the second example.
               | 
               | Also, notice that a linter and a compiler error are
               | different. I agree that people *should* always use a good
               | linter and IDE. The reality is sadly far from that.
        
               | funnymony wrote:
               | On its own, seeing 'SQLException' in method signature,
               | does not provide you with information: "this is part of
               | bigger transaction, and you have ability and
               | responsibility to retry or rollback".
               | 
               | That will be part of lib documentation, which should be
               | read & understood regardless of existence of checked
               | exception.
        
               | invalidname wrote:
               | No. The transactional context is something the caller to
               | the API knows. The person implementing the API that might
               | fail on the SQL call doesn't know what's going on. That's
               | why we would let the exception propagate.
               | 
               | > That will be part of lib documentation, which should be
               | read & understood regardless of existence of checked
               | exception.
               | 
               | First, checked exceptions *are* documentation for the
               | lib. The best kind of documentation.
               | 
               | Second, really?
               | 
               | I would love to live in your world where people read
               | documentation and where we all perfectly update the docs
               | for everything. But both sides of this equation leave a
               | lot to be desired in my world.
               | 
               | I don't read the documentation of most changes to most
               | libraries. If I did that I would never get anything done.
               | I can't even keep up with every commit that goes into the
               | project I'm running. There are too many changes and too
               | much code (I'm talking 30+ non-trivial merges per day).
        
             | funnymony wrote:
             | > important failure that can't be dismissed
             | 
             | But importance of the failure is determined completely by
             | the program, not the library.
             | 
             | Grep fails to open a file for reading -> message the user
             | and exit
             | 
             | Nuclear reactor controller fails to read important a file
             | -> initiate reactor shutdown or something.
             | 
             | If file read is critical, you have to handle failure no
             | matter what the interface is. Because you know that disk
             | can fail.
        
               | invalidname wrote:
               | Grep isn't a library. It's a program. Some things are
               | important in the library level e.g. an SQL exception must
               | have proper cleanup. However, in some cases you don't
               | want to cleanup. You might want to try a different
               | approach so generic cleanup code isn't necessarily the
               | right thing.
               | 
               | E.g. in the case of grep. Say I wrote grep and want it to
               | be generic. I wrote a library that implements grep. Then
               | I write a grep GUI tool. OOps. It exits if the file isn't
               | found instead of showing an error dialog. With exceptions
               | this is communicated up the layers. That's their purpose.
               | 
               | If I'm writing generic code and that code is used in a
               | nuclear reactor I would very much not like my failure
               | code to decide what to do. That's why we have exceptions,
               | they punt the responsibility to the next person up the
               | chain. I have no idea how to initiate a nuclear reactor
               | shutdown etc.
               | 
               | But as API authors how can we make sure the person who
               | writes the code up the chain knows that this is something
               | crucial?
        
               | funnymony wrote:
               | Yes, grep and nuclear reactor controller, and your gui
               | grep are applications.
               | 
               | They all use library for file reading.
               | 
               | As author of file reading library you don't know how
               | critical is the failure to open a file or how it should
               | be handled. My point was that You know those things only
               | at application level.
               | 
               | So it feels a bit misguided to decide at library level
               | which failures are important. (I.e. which are checked vs
               | which are unchecked)
               | 
               | > But as API authors how can we make sure
               | 
               | First thought: documentation. (which is required for
               | both, checked and unchecked exceptions).
               | 
               | But overall, it's not library author's responsibility or
               | ability to "ensure". You can't force correct handling of
               | exception. At best one can make it a bit more annoying to
               | ignore, and convenient to do the right thing.
               | 
               | My view:
               | 
               | - all exceptions should be unchecked
               | 
               | - assume all code throws
               | 
               | - if "log and exit/continue" is not enough and you need
               | to know exact exception types, dig into the docs.
               | 
               | - read docs during lib version upgrades
        
               | invalidname wrote:
               | The library knows which are crucial issues that warrant
               | your attention. A checked exception is a regular
               | exception, it's just one that you can't miss and must
               | either handle or propagate.
               | 
               | As a library author if you don't feel you need it then
               | don't use it. It's optional. That's the beauty of it.
               | 
               | > First thought: documentation. (which is required for
               | both, checked and unchecked exceptions).
               | 
               | Documentation < compiler checks.
               | 
               | > But overall, it's not library author's responsibility
               | or ability to "ensure".
               | 
               | Here we differ. Part of that is the domain we spend most
               | of our time in. If you're giving me the example of grep
               | then great, I see your point. Notice I'm giving an
               | example of a high availability huge enterprise app.
               | 
               | Assuming all code throws forces overly defensive code
               | which can cause a lot of problems. Yes, there's always a
               | "catch all" but that's not a valid case for these sorts
               | of apps.
               | 
               | Exit or continue are never enough for these sorts of
               | applications.
               | 
               | We have dozens of dependencies and sometimes more. Some
               | of them have mountains of documentations. Often we delay
               | updates since these are enterprises. Going over 3 years
               | of diffs in docs is just not a feasible option. Just the
               | diffs in Spring Boot alone and all its dependencies would
               | take several years.
        
               | scarface_74 wrote:
               | > The library knows which are crucial issues that warrant
               | your attention
               | 
               | The library author can't know what's crucial in the
               | context of my program.
        
               | invalidname wrote:
               | This feels argumentative but fine.
               | 
               | If you're unsure then make it a runtime exception. I
               | agree that a lot of the problems people have with checked
               | exceptions is that people over use them.
               | 
               | You don't want to handle the checked exception wrap it
               | with a runtime exception. But as an author if there's
               | something important, I want to give the user of the
               | library as much help as I can.
        
               | tremon wrote:
               | _But as an author if there 's something important_
               | 
               | You're still ignoring other people's point in this
               | thread: you (the author of a library/package) cannot
               | decide what is important for the users of your library.
               | You can guess, but your guess will always be wrong for a
               | subset of your downstream users.
               | 
               | And speaking from the other side, as an application
               | developer: for most of my code, an
               | ArrayIndexOutOfBoundsException is a pretty serious
               | exception. How do I go about making it a checked
               | exception?
        
               | magicalhippo wrote:
               | Indeed. For most applications, an out-of-memory exception
               | is typically one that's not necessary to handle and just
               | let it bubble to the top, as there's not much sensible
               | you can do if you can't allocate a few hundred bytes for
               | a new object.
               | 
               | However I've written several pieces of code where
               | allocating a large buffer could fail but where this
               | failure was not crucial. So I handled the out-of-memory
               | exception and just moved on.
        
               | scarface_74 wrote:
               | When I'm using grep, it's usually in the context of shell
               | script. For all intents and purposes it's a library.
               | 
               | I can choose to handle the error myself or let the shell
               | script crash by having "set -e".
               | 
               | Grep returns a non zero error code and it's still up to
               | me to decide how to handle it.
        
               | invalidname wrote:
               | That's your point of view. It's up to you. My experience
               | is different and often deals with handles very large
               | projects with serious continuous uptime. When you have
               | dozens of developers committing to a project and using
               | the APIs exiting with an error just isn't an option.
        
               | scarface_74 wrote:
               | I did say
               | 
               | > I can choose to handle the error myself
        
               | invalidname wrote:
               | That's not a practical strategy in a large system. That's
               | why we have exception handling.
               | 
               | When we write generic library code we have no way of
               | knowing how to handle an error in the full system. We
               | want to concentrate some of the error handling while
               | still making localized decisions in various places.
               | 
               | E.g. I want metrics and observability details updated,
               | yet I want locally to retry some behavior. Doing this
               | every time there's an error would force every library and
               | every part that can fail in my code to know how I plan to
               | handle the error.
        
               | scarface_74 wrote:
               | Isn't that what I just said? It's my responsibility as a
               | consumer of the library to decide how to handle it?
        
               | invalidname wrote:
               | Then I must have misunderstood you as speaking from the
               | perspective of the library. Sorry.
        
               | MrJohz wrote:
               | But the point is that in both cases the failure needs to
               | be handled somewhere and somehow. Surfacing the error
               | might be a valid option, but to make the decision you
               | need to know that the error is there in the first place.
               | 
               | This can be solved with good documentation, but it can
               | also be solved in the type system. Typically, if I can
               | get my computer to do work for me (e.g. make sure that
               | I've handled all possible error cases) then I'm much more
               | confident in my code, hence why I see a lot of value in a
               | well-designed checked exceptions system.
        
               | tadfisher wrote:
               | Exceptions aren't normal parts of the type system though,
               | they're a way to enforce control flow to deal with them.
               | Another way to do this is with a Result type, which would
               | actually be part of the same type system that you use in
               | the rest of the language.
        
               | rothron wrote:
               | > But importance of the failure is determined completely
               | by the program, not the library.
               | 
               | Exactly. I think this is the real crux about what's wrong
               | with checked exceptions. It puts the responsibility to
               | decide what exceptions are important on the library,
               | where it doesn't belong. Only the user of the library
               | knows that.
        
               | joebiden2 wrote:
               | Isn't it exactly the opposite? Checked exceptions are for
               | libraries to declare "exceptions" they can't handle
               | themselves. You, the user of the library, have to deal
               | with them (or declare them checked yourself1).
               | 
               | I'm not a friend of checked exceptions myself, but I
               | still think it's the opposite.
               | 
               | 1 which leads to the real issue with checked exceptions:
               | they propagate through dependencies, if one nested
               | dependency adds another checked exception, all
               | dependencies have to add the exception or handle it
               | themselves.
        
               | tremon wrote:
               | _Grep fails to open a file for reading - > message the
               | user and exit_
               | 
               | That should be: message the user and process the next
               | file
        
               | funnymony wrote:
               | :)
        
           | equalsione wrote:
           | This is what try-with-resource does in Java. Introduced in
           | Java 8 I think so better late than never
        
             | scarface_74 wrote:
             | Anytime I see "something was added to Java" late in the
             | game, it's usually an ugly hack.
             | 
             | But I have to admit that the syntax for it is actually
             | sensible.
        
         | anonymoushn wrote:
         | It's weird to argue that null is how the hardware works. Many
         | structs contain only bytes that are allowed to take on any
         | value, so making them nullable takes extra space.
        
           | invalidname wrote:
           | This is mentioned in the memory layout section in the post.
        
             | anonymoushn wrote:
             | I'm not sure what "a marker" is and how to make it take 0
             | bits of storage.
        
               | invalidname wrote:
               | This is an upcoming behavior in Valhalla which isn't yet
               | a part of Java so there's nothing final. Currently
               | Valhalla has primitive and value object types that
               | determine identity behavior.
        
               | anonymoushn wrote:
               | You mean non-nullable object types then. Which are faster
               | and take up less space because that's how the hardware
               | works :)
        
               | invalidname wrote:
               | They're not faster for all cases. Near memory works great
               | for some edge cases such as looping on an array of data
               | in a block.
               | 
               | This is very much a special edge case. For many other
               | non-benchmark situations this isn't so simple. If you add
               | the overhead of memory copying and lack of identity
               | things are more complex.
        
       | aib wrote:
       | I think the reason is simpler: Unchecked exceptions.
       | 
       | Investing in (checked) exception safety is not as valuable when
       | anything can throw an NPE.
        
       | necovek wrote:
       | I am surprised an even more obvious interface definition of
       | public interface ExampleInterface {           void foo() throws
       | IOException, AnotherException;       }
       | 
       | was not covered: is this also not supported?
       | 
       | That would solve a lot of use cases if inheritance is respected.
        
         | ailef wrote:
         | This is indeed the supported way to do it.
        
           | necovek wrote:
           | Thanks for confirming as I am not familiar with Java, but
           | good to know it does make sense. As such, I am not sure what
           | did the original author complain about because that's
           | perfectly sufficient language support.
        
         | masklinn wrote:
         | It is supported, but does not actually _solve_ anything, you
         | 're still limited to whatever checked exceptions the creator of
         | the interface has decided might be valid (it's just that in the
         | original it's "none").
         | 
         | Let's say that the creator of the interface allows IOException,
         | but the backend of my implementation is a database so I have
         | SQLExceptions, same issue.
         | 
         | Is the creator of the interface supposed to add every checked
         | exception in the standard library? Ignoring that this still
         | isn't all of them, then the _caller_ is hosed because they have
         | to _handle_ (or rethrow) every single one of them which is no
         | better. So they probably go  "fuck it" and just handle /
         | rethrow Exception. At which point you can just do that on the
         | interface, and it's not helping anyone, and you're better off
         | just not saying anything.
         | 
         | Checked exceptions simply don't mesh with the language: because
         | the language provides limited to no way to abstract over them
         | they're an issue every time you're trying to be generic over
         | anything, the only situation in which they kinda sorta work is
         | if the entire callchain is concrete, which not only is very
         | limiting but it's very much _not_ idiomatic.
        
           | throwaway290 wrote:
           | Maybe your SQLException should be caller's IOException? I
           | don't code Java but IMO callers should be hit with errors at
           | the right abstraction level. If caller is using that
           | interface and not your low-level backend functions directly
           | then caller should expect IOException not SQLException.
           | 
           | If the interface does not provide exception types for all
           | cases so you are stuck rethrowing your error as a wrong one,
           | and I'd say that means the interface is bad but not checked
           | errors are bad...
        
             | funnymony wrote:
             | "bad" might not be the right word. Maybe better "usefulness
             | for current population of developers is very limited".
        
           | maratc wrote:
           | > Let's say that the creator of the interface allows
           | IOException, but the backend of my implementation is a
           | database so I have SQLExceptions, same issue.
           | 
           | The creator of the interface should decide on a generic
           | exception type:                 public interface
           | UniversalStorageInterface {           void store() throws
           | StorageException;       }
           | 
           | Then, in the present, the FileStorage can define (and throw)
           | a FileStorageException (inherits from StorageException), and
           | the SqlStorage may define (and throw) a
           | DatabaseStorageException (same).
           | 
           | In the future, where we might want to store everything on a
           | (non-existing yet) Cerulean backend, we would then define
           | (and throw) a CeruleanStorageException (again, an
           | implementation of StorageException), and our basic Interface
           | would not need to change. We would also have no need to
           | recompile FileStorage or SqlStorage (or proprietary SteelBlue
           | storage where we have no code).
        
             | masklinn wrote:
             | So now whoever creates the interface needs to define an
             | exception type per method, and whoever implements the
             | interface needs to define a subclass of that per method and
             | catch-and-wrap every exception their callee raises.
             | 
             | You better add some serious tooling built into the language
             | to facilitate this, because from experience ain't no way
             | anyone's going to bother with this if they have to handroll
             | it, even with IDE codegen assisting.
             | 
             | And it still doesn't solve the issue of generic interfaces
             | like streams.
        
               | necovek wrote:
               | Whether you do it with exceptions hierarchy or error
               | types or something else, it's pretty much the same thing:
               | you either handle all the errors or not (or anything in
               | between).
               | 
               | How do you solve that error complexity with any other
               | approach and how is that different using any other syntax
               | sugar?
               | 
               | The only thing I dislike about exceptions is that they
               | are ignored by default allowing people to all too easily
               | write code with no error checking.
        
               | maratc wrote:
               | It doesn't have to be a type _per method_ , and if
               | someone is "going to bother with this" depends on the
               | application -- I guess in many cases it's perfectly fine
               | to write a happy code with a single catch where you
               | present a sad-face emoji and "try again later" line; in
               | other types of applications this just won't fly.
        
               | necovek wrote:
               | Note that StorageException in the previous example could
               | also simply be a BaseStorageException that all the
               | methods throw, even if one of them is
               | DiskFullError(BaseStorageException) or
               | ObjectStorageLimitExceededError(BaseStorageException),
               | and another is
               | InvalidFilenameError(BaseStorageException).
        
         | PretzelPirate wrote:
         | Wouldn't it still be a problem? By putting "throws [Exception
         | List]" in the interface, the interface making assumptions about
         | the implementation details of every potential implementation.
         | 
         | The interface can't know every exception that an implementation
         | might throw, and using your example, some implementations may
         | not use IP at all and won't throw those exceptions.
         | 
         | It seems like you'd end up needing to list every possible
         | exception on "foo() " when defining the interface, and then
         | handle every possible exception in any code that uses
         | "ExampleInterface".
         | 
         | At that point, it would be better to not annotate exception
         | information at all.
        
           | thewix wrote:
           | Exceptions should absolutely be a part of an interface and it
           | stops you from leaking implementation details. If I have an
           | IRepository interface then none of the methods should ever
           | throw SqlException, HttpException, or anything like that.
           | You'd map them to something else. Exceptions in most
           | languages are implicitly part of the contract, and not having
           | consistent ones on an interface breaks Liskov's Substitution
           | Principle.
        
           | grumbel wrote:
           | > the interface making assumptions about the implementation
           | details of every potential implementation.
           | 
           | No, as you'd force the implementation to catch their
           | implementation specific exceptions and rethrow them in the
           | way defined by the interface.
           | 
           | That's really the crux of it, when I call foo(), I expect to
           | see a FooException when something goes wrong, since that's
           | the only one I can meaningful react to. If I get an
           | ImplementationDetailException that I never heard from and
           | that isn't specified in the interface, how am I supposed to
           | react to that in a meaningful way?
           | 
           | If exception are supposed to be used for error handling, you
           | have to actually report and handle them in a well specified
           | manner, you can't just treat them as a slightly less crashy
           | version of a SIGSEGV.
        
             | exceptotron wrote:
             | I agree that, if you are working with checked exceptions,
             | that's certainly better than adding garbage to your
             | interface.
             | 
             | But for many types of checked exception you just end up
             | wrapping the underlying exception in a foo exception
             | anyway.
             | 
             | I think you are right: if you are using exceptions for
             | error handling, of errors you expect people to actually
             | deal with, this is currently a reasonable (if painful)
             | option to ensure exhaustive analysis of returns -- which
             | most people agree is a good thing these days I think, what
             | with mainstream popularization of options etc.
        
             | masklinn wrote:
             | Unless the language does that for you (both enforcing it,
             | and providing tools to make it absolutely trivial), it will
             | _never_ happen, it 's just way too much overhead to have to
             | define a new exception for every method in an interface,
             | then a sub-exception for every method in the implementation
             | of that interface, then go through the internal conversions
             | from the underlying exceptions to the parent one.
             | 
             | People can't be arsed to do that in Rust where there's ways
             | to abstract over those things and macros to define them.
             | 
             | If that's the route you assert is necessary, you need
             | something like Zig's errors.
        
               | necovek wrote:
               | What you are saying is that people can't be arsed to do
               | proper error checking, which is absolutely true.
               | 
               | Making a sane hierarchy of exceptions is the smaller of
               | the problems of actually handling them everywhere.
        
           | necovek wrote:
           | As others have pointed out, my example would have been better
           | if it said `ExampleExceptionFoo, ExampleExceptionBar` to more
           | clearly indicate that an interface would define what
           | exceptions it could throw as part of the contract. Individual
           | implementations could extend those base exceptions if they
           | need to.
           | 
           | There is not much difference between this and using error
           | types, except the syntax sugar.
        
       | breadwinner wrote:
       | I have extensive experience in C# as well as Java. It is bizarre
       | to suggest that checked exceptions have failed. It is unchecked
       | exceptions that have failed. It is the biggest flaw of C#, in
       | fact. Why?
       | 
       | As an example, I wrote some very good C# code, carefully tested
       | it, made it work flawlessly, then suddenly it started crashing.
       | What happened? Someone made a change in a function I was calling,
       | and it started throwing a new exception. This would have caused a
       | compile error in Java, not a crash.
       | 
       | The list of recoverable exceptions that can be thrown by a method
       | should be part of the contract. It should be written down by the
       | programmer, and enforced by the compiler. If not, then to avoid
       | crashing you would have to catch the root Exception class, which
       | everyone agrees is a bad idea.
       | 
       | More on checked vs unchecked exceptions here:
       | https://forum.dlang.org/thread/hxhjcchsulqejwxywfbn@forum.dl...
        
         | beached_whale wrote:
         | Why not catch System.Exception also? Or just catch
        
           | breadwinner wrote:
           | When you catch the root Exception class in C# you end up
           | catching IndexOutOfRangeException as well. You should let the
           | program crash instead because this happened due to a bug in
           | your program. Continuing as if nothing happened is unsafe.
           | 
           | Another issue is that you are not allowing higher layers see
           | the exception, even though they may have logic for recovering
           | from the exception. To see the exception they have to now fix
           | your code by removing the catch-all.
        
             | beached_whale wrote:
             | I meant at the higher layers, minus some potential
             | cleaning, a lot of library code is exception neutral and
             | shouldn't care
        
             | Kwpolska wrote:
             | Catch the root Exception class, show an error, but let the
             | user continue working if possible. For a web app, there
             | already is a catch-all exception handler at the request
             | level that prevents the entire server from crashing. For
             | other environments, having a catch-all handler at some top-
             | level interaction point (i.e. on buttons or menu items)
             | would be a good choice too.
        
               | breadwinner wrote:
               | If the exception that was thrown (by a method you called)
               | is in fact a condition your code can recover from, then
               | the functionality of your code was needlessly aborted.
        
               | wredue wrote:
               | The "if it's possible" is a major part of the reason to
               | not catch the root exception.
               | 
               | Catching typed exceptions give you much more easily
               | parsable details about whether or not "it's possible" to
               | recover.
               | 
               | It's bad practice to catch the root exception. More often
               | than not, it points to an inexperienced developer
        
         | funnymony wrote:
         | "I wrote some very good C# code"
         | 
         | We all do.. its always that other guy who is at fault :)
        
         | Kamq wrote:
         | > Someone made a change in a function I was calling, and it
         | started throwing a new exception. This would have caused a
         | compile error in Java, not a crash.
         | 
         | I call BS here. I've worked in a few Java projects, and in
         | every single one, the people changing the method in question
         | would have thrown RuntimeException to stop the compile errors.
         | 
         | If RuntimeException was checked, you might have a point, but
         | given that there's a class of unchecked exceptions, you're
         | relying on discipline instead of the compiler anyways.
        
           | breadwinner wrote:
           | You need to help train your coworkers to write better code :)
           | Point this thread out to them.
           | 
           | Seriously, Java doesn't force bad programmers to write good
           | code. It just enables good programmers to write good code.
        
             | anonymoushn wrote:
             | They also have to throw RuntimeException if they want to
             | use APIs that accept functions and therefor specify which
             | checked exceptions those functions are allowed to throw,
             | like Streams.
        
             | Kamq wrote:
             | I will acknowledge that this wasn't the best code (or the
             | best programmers).
             | 
             | I will also cede the point that you can write good code in
             | Java.
             | 
             | But my point was that Java wouldn't have enforced the
             | situation to be a compile error, not that you can't write
             | good code in Java. I maintain this point, and I think it's
             | true for the average java project.
             | 
             | If you're getting these benefits from Java, it's the
             | intersection of Java and your team's or your organization's
             | culture, not purely from Java. It's possible for you to
             | gain this benefit and for checked exceptions to have still
             | failed out in the wider world of the average Java project.
        
             | xen0 wrote:
             | And a lack of checked exceptions means good programmers
             | can't write good code? So good code may only be written in
             | Java?
             | 
             | No... I don't think so.
             | 
             | Checked Exceptions are at best, just a 'hey, are you sure
             | that's right thing to do here?' and at worst, an additional
             | source of rigidity in your code that blows up the scale of
             | a minor change.
        
               | breadwinner wrote:
               | > _And a lack of checked exceptions means good
               | programmers can 't write good code?_
               | 
               | Right, and that was my point at the top post in this
               | thread.
        
               | xen0 wrote:
               | Wait, you seriously hold that position!? As in, only Java
               | permits good code?
        
               | breadwinner wrote:
               | Each language has its strengths and weaknesses. In the
               | case of exceptions Java gets it right, C# doesn't. There
               | are other aspects that C# does better than Java.
        
             | crabbone wrote:
             | That is not better code. Couple of try-catch-catch-
             | catch-...-catch cases in a function that would otherwise be
             | 2-3 lines long makes for an awful code.
             | 
             | Also, imagine a situation when an iterator throws an
             | exception. Or you wanted to write f(g()) but now you can't
             | and have to do:                   try:             T t =
             | g();         catch E1:             ...         catch En:
             | ...         f(t);
             | 
             | Now it takes a much greater effort for the reader to figure
             | out what's going on. It's also tempting to allow some
             | exception cases to succeed so, f(t) is reached even if
             | there was an error. Because these two code pieces are now
             | far apart, it's possible that later edits will introduce
             | bugs because the programmer didn't see that either f(t) is
             | reachable even if g() failed, or accidentally made it
             | reachable when it shouldn't have been.
             | 
             | Bottom line, it makes human errors more likely.
        
               | mkleczek wrote:
               | This is only because you use exceptions incorrectly. You
               | can (and should) write:
               | 
               | try {                 final var fResult = f(g());
               | //do something with fResult
               | 
               | }
               | 
               | catch (E1 e) {...}
               | 
               | catch (En e) {...}
               | 
               | That's the main idea of exceptions in all languages: main
               | flow is kept together and exceptional flows are separate.
        
               | AlphaSite wrote:
               | I think there's a strong assumption in this pattern that
               | can and should be handled immediately and that there is a
               | recovery path from the failure, if there is no recovery
               | path and you're just propagating the error this pattern
               | becomes an awfully verbose return statement.
        
               | mkleczek wrote:
               | When structured exception handling was becoming popular
               | (C++ in nineties?) the idea was that immediate error
               | handling (like when you have to check return values for
               | errors) makes the main flow (happy path) blurred and
               | unreadable.
               | 
               | You cannot have both at the same time, I'm afraid.
        
               | im3w1l wrote:
               | I think the issue with this pattern is that either we
               | don't care about went wrong, we want to treat all errors
               | the same way, and then having n catch clauses is
               | overkill. Or we do care about went wrong, and in that
               | case the exception type is not enough to identify the
               | issue, as often multiple lines in the try block will
               | throw the same exception type.
        
               | mkleczek wrote:
               | Yes - that's one of the issues with structured exception
               | handling. Relying only on exception type itself is not
               | enough as information is lost (ie. the actual source of
               | exception)
        
               | im3w1l wrote:
               | Crazy idea: Maybe it would be helpful if we could label
               | operations, and catch by either _label_ or _type_ or
               | _(label, type)_. The we could centralize error handling
               | without losing anything.
        
               | mkleczek wrote:
               | While deceptive at first... Just try to imagine how
               | "extract method" refactoring would work...
        
               | im3w1l wrote:
               | It would have to do some clever rewriting of the logic or
               | it could simply declare that extracting method isn't
               | available on that region. The simplest way to rewrite the
               | logic may be the introduction of new exception type(s)
               | corresponding to the labels.
        
         | dbrueck wrote:
         | I stopped using Java a long time ago, and so I assume the
         | language has gotten better since then, but early on at least it
         | felt like Java almost took pride in making the developer jump
         | through extra hoops. Compared to many other languages, using
         | Java just made me feel tired.
         | 
         | Checked exceptions - a feature that seems to be a cost to the
         | developer 100% of the time while being a benefit far less than
         | 1% of the time - is a quintessential example of Java's tendency
         | to cause developer fatigue.
        
           | breadwinner wrote:
           | Some people say the same about strong typing. Like, why do I
           | have to write down the type of every single parameter or
           | variable? Java is making me jump through hoops!
           | 
           | The point is, if you don't need the rigor of a strongly typed
           | compiled language, there are other languages you can use.
           | Perhaps a bash script is all you need.
        
             | ablob wrote:
             | In strong typing you don't have to (See type inference). It
             | just means that the shape of the data/object you receive is
             | known to you.
             | 
             | That a lot of languages force you to write it out is not an
             | inherent property of strong typing.
        
             | wiseowise wrote:
             | Java is the only mainstream language with checked
             | exceptions and checked exceptions are nowhere near
             | usefulness of static typing.
             | 
             | Following your logic, Java developers can now say "if you
             | don't like freedom of Java, use Rust/Haskell/Scala to
             | validate everything at compile time"
        
               | lostmsu wrote:
               | You could say Rust error handling is more like checked
               | than unchecked exceptions.
        
               | wiseowise wrote:
               | It is, that's the whole point.
        
               | crabbone wrote:
               | Java is not the only language that has checked
               | exceptions. C++ has them too, for example, but thanks god
               | virtually nobody uses them in C++.
        
               | gpderetta wrote:
               | C++ never had checked exceptions. It had dynamic
               | exception specifications but they were a very different
               | (and useless) thing as they were runtime checked not
               | statically.
               | 
               | I wish C++ had checked exceptions.
        
               | nemetroid wrote:
               | Checked exceptions (i.e. "dynamic exception
               | specification") were deprecated in C++11 and removed in
               | C++17.
        
               | treis wrote:
               | Checked exceptions are part and parcel of a type system,
               | though. If you have a foo function that returns a value
               | or error you've got different options. Pseudo code they
               | are:
               | 
               | foo():boolean throws Some, List, Of, Errors
               | 
               | foo():boolean | Some | List | Of | Errors
               | 
               | foo():(boolean | nil) , (nil | Some | List | Of | Errors)
               | 
               | The first is checked exceptions. The second is returning
               | different types. Typically there's some sort of Option
               | wrapper for ergonomics. The third returns a result, err
               | tuple and by checking if err is nil you can see if foo
               | succeeded.
               | 
               | Ultimately they are all the same. What differs is the
               | boilerplate/syntax to accomplish what you want. If what
               | you want is a generic error to handle unknown events then
               | you gotta write it that way no matter which system you
               | use.
        
             | dbrueck wrote:
             | > The point is, if you don't need the rigor of a strongly
             | typed compiled language
             | 
             | This has nothing to do with strongly typed compiled
             | languages vs alternatives.
        
             | crabbone wrote:
             | You are very confused... "strong" in strong typing doesn't
             | mean you have to write much or at all. Actually, it doesn't
             | mean anything really. But, let's say, Haskell is probably
             | at least as "strongly typed" as Java -- at least that's how
             | most people understand that wording. And you don't have to
             | write types in Haskell at all. It will be a nightmare (as
             | if Haskell can be anything else, but even by the very low
             | Haskell standards, it would still be a nightmare) code, but
             | it will "work".
             | 
             | Until very recently, Java was just tedious, repetitive,
             | high-entropy language, where you had to write everything
             | multiple times.
             | 
             | Eg.:                   VeryLong<Type> variable =
             | VeryLong<Type>();
             | 
             | In this expression, it's obvious what would be the most
             | likely type of variable, but you still have to write it
             | twice -- which is just worthless use of your time,
             | especially when trying to read this very repetitive mess,
             | and very easy to make a typo.
             | 
             | Recently, Java tried to improve this situation by allowing
             | to omit types when the type inferred by default is the one
             | you want. So, you need to write less. You also need to
             | learn the inference rules, obviously, so it increases the
             | cognitive load and rises expectations towards programmer's
             | skill a bit. But, I think it's fair, since Java from a
             | language for the brain-dead transformed into a language
             | that requires more effort to master. While I'm not sure
             | it's a positive change... this decision by Java developers
             | kind of flies in the face of your argument:
             | 
             | No, it's not necessary to repeatedly spell out what you
             | want your program to do. It's just boring and contributes
             | nothing to the program's correctness. If anything, it only
             | causes the programmer to lose focus and introduces
             | mechanical errors that would've been totally avoidable, had
             | the language was more terse.
        
               | breadwinner wrote:
               | Type inferencing can get you in trouble quickly. Consider
               | this code:                  var fireable = someMethod();
               | firable.fire();
               | 
               | The programmer intended this code to fire an employee.
               | Let's say this code is in a military application, and
               | another programmer modified the someMethod() function to
               | return a missile. As long as the missile object has a
               | fire() method this code will compile just fine... and do
               | something the code didn't intend to do.
               | 
               | How likely are you to have employee firing and missile
               | firing in the same program? Not very likely, but the
               | principle is valid, regardless. You need to express your
               | intention more clearly like this:
               | Employee fireable = someMethod();
               | 
               | Now if someMethod() is modified to return a missile you
               | get a compilation error, and the world will be a lot
               | safer. You don't want missiles being fired by accident!
        
               | ajanuary wrote:
               | someMethod() could return an implementation of Employee
               | that overloads the fire method to dispatch missiles.
               | 
               | The point being, someMethod is doing some job that you
               | want it to do. It is incredibly unlikely to
               | simultaneously start doing a completely different job,
               | while also returning something of the same shape.
        
               | breadwinner wrote:
               | > _an implementation of Employee that overloads the fire
               | method to dispatch missiles_
               | 
               | If you intentionally break it, that's on you.
               | 
               | > _It is incredibly unlikely to simultaneously start
               | doing a completely different job, while also returning
               | something of the same shape._
               | 
               | But it doesn't need to return something of the same
               | shape! The shape is not being checked. All the compiler
               | checks is for the existence of a single method of the
               | same name. You call that type checking?
        
               | somehnguy wrote:
               | This is kind of a ridiculous thing to worry or think
               | about. I'm seeing a lot of type inference in modern Java,
               | and it's the default way of writing Kotlin.
        
               | breadwinner wrote:
               | It is dangerous to call a method on an object whose type
               | is not being checked by the compiler. Whether that's a
               | ridiculous thing to worry about depends on how important
               | your program is. If your work for NASA and your code will
               | run inside the Mars rover, it is not ridiculous to worry
               | about such things, but if you're writing some code that
               | will be used once and thrown away then yeah, it might be
               | ridiculous.
        
               | vips7L wrote:
               | > Until very recently, Java was just tedious, repetitive,
               | high-entropy language, where you had to write everything
               | multiple times.
               | 
               | More than half a decade ago.
        
               | esafak wrote:
               | Which means today's code.
        
               | Akronymus wrote:
               | F# is strongly typed as well, and I almost never have to
               | write type annotations. Hindley milner type inference is
               | pretty useful. And my IDE of choice also shows the
               | inferred types for easy checking. Altough, without that,
               | it'd be a PITA, as you said about haskell.
        
             | ok123456 wrote:
             | If checked exceptions were implemented in such a way that
             | they were inferred, and they weren't erased in the byte
             | code, much like how people use strong types now, they
             | wouldn't be seen as hoops. Meaning, if you didn't
             | explicitly handle a checked exception, it would
             | automatically bubble up yet be visible to your IDE.
             | 
             | I would love if, at design time, all the possible
             | exceptions that could happen were able to be inferred. But,
             | the way it happens in java, it's entirely manual, and it
             | won't catch things where I'm referencing compiled code. The
             | design of checked exceptions in Java lead to all kinds of
             | exception anti-patterns becoming common, just to shut up
             | javac.
        
               | LorenPechtel wrote:
               | Exactly. I have no problem with a function declaring the
               | sort of exceptions it can handle, although simply
               | handling them when they occur is normally good enough.
        
             | gjvc wrote:
             | _Perhaps a bash script is all you need._
             | 
             | Brutal, even for this site
        
           | narag wrote:
           | _Java almost took pride in making the developer jump through
           | extra hoops_
           | 
           | That's exactly it. A holier-than-thou stance, reprised by
           | most comments in this post.
           | 
           | Checked exceptions were a PITA.
           | 
           | Someone has recommended to use another language if you didn't
           | like it. Unfortunately my employer at that time had entered a
           | partnership with Sun that prevented that :)
           | 
           | Another ideological prohibition: no pointers. Well, how do
           | you assign event handlers then? People were extremely
           | confused with anonymous classes. I got the reason instantly.
        
           | naasking wrote:
           | > Checked exceptions - a feature that seems to be a cost to
           | the developer 100% of the time
           | 
           | That's because exceptions are overused for not-truly-
           | exceptional conditions. Languages that throw exceptions when
           | a dictionary does not have a key you're looking for are doing
           | it wrong.
        
         | Kwpolska wrote:
         | > The list of recoverable exceptions that can be thrown by a
         | method should be part of the contract.
         | 
         | If it's really recoverable, then should it actually be an
         | exception? How often do you see exceptions that are
         | recoverable?
         | 
         | > If not, then to avoid crashing you would have to catch the
         | root Exception class, which everyone agrees is a bad idea.
         | 
         | I disagree that it's a bad idea, having a catch-all exception
         | handler to avoid crashing the entire app is a good idea.
         | 
         | Java also has RuntimeException, and with it, a whole host of
         | exceptions that may occur but are not declared. Should every
         | function that uses the division operator be marked with `throws
         | ArithmeticException` since it may end up being a division by
         | zero? That would quickly become absurd.
        
           | kaba0 wrote:
           | Yes, in Java's parlance an Exception is a recoverable code
           | path, Error is the kind that is not.
           | 
           | E.g. a network call failing due to some IOException can be
           | easily retried, that's a proper error handling.
        
             | ahtihn wrote:
             | How are you supposed to recover from
             | IllegalArgumentException or NullPointerException?
        
               | kaba0 wrote:
               | Recoverable, as a technical term. You can catch an NPE
               | and follow it with any kind of code you wish, it will be
               | correctly "handled" by the VM. (The semantics of course
               | depend on the exact specifics, but returning a server 500
               | error and not failing the process for example in a web
               | server is completely valid).
               | 
               | You can try catching an OutOfMemory _Error_ , but it is
               | not guaranteed that your handling code can successfully
               | run. Hence, non-recoverable.
        
           | chongli wrote:
           | _If it's really recoverable, then should it actually be an
           | exception?_
           | 
           | That's the problem with exceptions in general. They're a
           | hammer that makes everything look like a nail. People start
           | using them for all kinds of control flow situations because
           | they're more convenient than having to deal with a lack of
           | type system support for optional values, etc.
           | 
           | You get to the point where you have a parser that is
           | expecting a digit and it encounters an alphabetic character
           | so it throws an exception!
        
             | saurik wrote:
             | There are remarkably few situations involving recoverable
             | errors, though, and they almost all look like "not found"--
             | which includes your parse integer example--and, even then,
             | most of the time you will want a forceful automatic
             | exception variant and not a "recoverable" error value as
             | there is almost never anything to do instead: try-parse is
             | so rare of a thing that is correct to do it warrants having
             | a quick "try" attached to the expression.
             | 
             | In contrast, there are a billion ways in which almost every
             | line of code can fail--even stuff people are super sure
             | _could never fail_ is still usually at least subject to
             | stack exhaustion--and almost none of these failures are
             | things you should locally  "handle"; and yet in many--even
             | most--cases these are situations you can still recover from
             | at a higher level if you don't screw up your own logic
             | (which is not the point of these errors).
        
               | Akronymus wrote:
               | A parser failing is something I'd expect to be
               | recoverable. And I'd expect it to return where it failed
               | and why. You can do that without throwing exceptions at
               | all, and instead encode it in the type. Like returning a
               | result<parsedObject,failureReason>. (Can be further
               | refined, like adding the already parsed/yet to be parsed
               | string to the failurereason as a value) I WANT to be
               | forced to handle it right there and then rather than
               | potentially forgetting a try catch, or checking the
               | boolean returned if it was successful or checking the out
               | parameter if it is not null (boolean tryparse(out
               | parsedObject) is something I see commonly)
               | 
               | For me it is much more ergonomic to use a
               | match tryparse somestring with       | Ok obj ->
               | proceedFurther obj       | Error reasons -> handleerror
               | reasons
               | 
               | For truly exceptional cases you don't expect to be
               | recoverable? Yeah, throw an exception.
        
         | osigurdson wrote:
         | Exceptions are great when they don't need to be caught.
         | Otherwise they are a pain.
        
         | crabbone wrote:
         | This is very simplistic view of the problem. This completely
         | glosses over modularity, ABI, performance optimizations... just
         | to name a few.
         | 
         | How are you going to write generic functions that take
         | functions as arguments and re-throw the errors thrown by these
         | functions, if you use checked exceptions? Will you require that
         | the acceptable functions _only_ throw exceptions that you like?
         | -- Then your generic function is close to being worthless...
         | 
         | If exceptions are encoded in function's interface, then they
         | have to be in ABI, but then you _must_ have non-trivial types
         | available when marshalling data between two components, so, you
         | cannot serialize the communication using some protocol with a
         | fixed number of types (eg. JSON and friends), because now you
         | need to account for the infinite variety of exception types.
         | 
         | Because of at least these two things, what I saw happen a lot
         | of Java / C++ projects (god blessed me with very little C#
         | exposure) was that as soon as a developer encountered a
         | function with checked exceptions, a wrapper was written which
         | changed the type into a runtime exception. This is so because
         | exceptions are supposed to be handled separately, and often the
         | author of the function has no idea how they need to be handled
         | -- so they want to concentrate on the main goal of the
         | function. Once functions grow into garlands of try-catch-catch-
         | catch-...catch the focus is lost. It becomes very hard to
         | understand why the function was written in the first place,
         | because the error handling takes over every other concern.
        
           | duped wrote:
           | > Will you require that the acceptable functions only throw
           | exceptions that you like? -- Then your generic function is
           | close to being worthless...
           | 
           | Constrained generic parameters are actually super useful.
           | 
           | > If exceptions are encoded in function's interface, then
           | they have to be in ABI
           | 
           | They already are in Itanium
           | 
           | > you cannot serialize the communication using some protocol
           | with a fixed number of types (eg. JSON and friends), because
           | now you need to account for the infinite variety of exception
           | types.
           | 
           | No, the only exception that arises is ser/deserialization
           | error. You can also trivially represent an error in JSON
           | using an object, which is exactly what JSON RPC protocols do.
           | It's also never safe to throw across an FFI boundary and
           | similarly nonsensical to throw across a serialization
           | boundary, so I'm not sure why you'd care.
        
           | interlocutor wrote:
           | > _write generic functions that take functions as arguments
           | and re-throw the errors thrown by these functions_
           | 
           | There is a philosophy that applies here: simple things should
           | be simple, complex things should be possible. The scenario
           | you're mentioning is not common enough that the language
           | design should be centered around it.
        
             | newaccount74 wrote:
             | It's a very common pattern, almost every modern language
             | has a "map" function. The map function can throw a superset
             | of the exceptions that its argument can throw. If checked
             | exceptions can't deal with this, they'll be of limited use.
        
               | interlocutor wrote:
               | In the case of map function hopefully you're using it
               | with methods that don't fail in serious ways, and don't
               | need strong error recovery. If so Java has
               | RuntimeException to handle that case. If serious errors
               | are possible and strong error recovery is needed, then
               | you need to avoid the conveniences offered by functional
               | style programming.
        
               | Quekid5 wrote:
               | The problem is that if some code you call throws a
               | checked exception (InterruptedException being an
               | _extremely_ common culprit) then you _must_ wrap... and
               | suddently _nobody_ calling YOUR code can catch that
               | InterruptedException reliably because it 's now a
               | SomeException (doesn't even have to be RuntimeException
               | specifically) with an added "suppressed" exception that
               | you now have to check for.
               | 
               | ... so the basic "catch" syntax starts to fall apart
               | because now you have to catch everything and resort to
               | stuff like Guava's Throwables helpers.
               | 
               | It's madness.
               | 
               | The problem ultimately is variance: Methods are
               | covariant, but throws clauses _must_ be contravariant.
               | 
               | There _are_ ways to solve this but  "checked exceptions"
               | (as in Java) are _not_ the right way. Ask anyone who 's
               | worked in Scala on the JVM which they prefer and you'll
               | have your answer.
        
               | dasyatidprime wrote:
               | It's possible to use a type parameter in a throws clause
               | in Java, last I checked (as I also mentioned in more
               | detail above). But it doesn't seem to be common practice,
               | so interop is still a disaster.
        
               | bsder wrote:
               | > The map function can throw a superset of the exceptions
               | that its argument can throw.
               | 
               | Wait. Why should a map care about what exceptions it's
               | arguments can throw?
               | 
               | A map is storing a thingit. A thingit should exist
               | independently before it gets placed into a map. Placing a
               | thingit into a map should not invoke anything on the
               | thingit. The only exceptions coming back from attempting
               | to place a thingit into a map should be exceptions caused
               | by the map.
               | 
               | What am I missing?
               | 
               | Obviously, there are maps that conflate themselves and do
               | things like take ownership when an object is placed into
               | the map. But that's not the general case and presumably
               | you wrote the map specifically with that in mind.
        
               | hollerith wrote:
               | You're thinking of a finite map, also called a hash table
               | or a dictionary.
               | 
               | The map function takes a function and a list and applies
               | the function to every element of the list:
               | map(double,[1,2,3]) = [2,4,6]
               | 
               | Grandparent could've used the for loop rather than the
               | map function to make his point:                 for i in
               | [1,2,3]:           print i * 2
               | 
               | -- because in general instead of the i * 2 we might have
               | a call to a function that might raise an exception.
        
           | dasyatidprime wrote:
           | > How are you going to write generic functions that take
           | functions as arguments and re-throw the errors thrown by
           | these functions, if you use checked exceptions?
           | 
           | I've actually done this with an interface type parameter used
           | in a throws clause in Java before, but I'm not sure how it
           | interacts with module boundaries, it's kind of verbose, and
           | it certainly didn't seem to be common practice. But it did
           | work: the HOF-ish method that took a parameterized
           | ThingFrobber<E> and used it to frob things unchecked would
           | compile only if it also threw E, and the method passing the
           | HOF a ThingFrobber (I think via lambda syntax) could catch
           | the associated concrete checked exception type to sink it.
           | IIRC I was using it to allow a checked early exit from a
           | complex iteration, and it even propagated a slightly complex
           | bound with multiple checked exception types more smoothly
           | than I'd expected it to.
        
             | iainmerrick wrote:
             | Right, I was thinking that this is _the_ answer to the
             | supposedly intractable problem posed by the original
             | article.
             | 
             | Just make your ExampleInterface generic --
             | ExampleInterface<T>.foo() throws T.
             | 
             | That technique works really well, and I don't understand
             | why it isn't more widely used (or at least it wasn't when I
             | last used Java for serious work).
        
         | _a_a_a_ wrote:
         | Each 'leaf' statement of language should have a definite and
         | finite list of exceptions that it can throw.
         | 
         | Each non-leaf statement is a composition of leaf statements.
         | Therefore every exception can be determined, and that's our
         | checked exceptions would work; work at the exceptions and check
         | they match the list.
         | 
         | Therefore even without checked exceptions the compiler could
         | provide the exceptions you want and you can check them or not
         | _if you please_ , so get the best of both worlds. All without
         | forcing people to give an explicit list of exceptions
         | everywhere. Forcing checked exceptions is not going to be
         | popular, and much of the time is counter-productive.
         | 
         | Also someone broke your Liskof Substitution Principle which may
         | be your bigger problem.
        
         | scarface_74 wrote:
         | Catching the root exception is not a bad idea. If you had
         | caught the new exception what would you have done with it?
         | 
         | Most of the time, it's either catch the exception, log it in a
         | central logging system keep moving and have a central alerting
         | system or catch the exception log it and crash the program.
        
           | simias wrote:
           | > Catching the root exception is not a bad idea. If you had
           | caught the new exception what would you have done with it?
           | 
           | Something relevant to the error condition, probably?
           | 
           | There may be some cases where the total set of possible
           | errors is too large to meaningfully handle every one of them
           | specifically (for instance if you call a high level GPU
           | initialization routine that can fail in a myriad of ways) but
           | that's not true in all cases. Maybe the new error was in fact
           | recoverable, or maybe it calls for some more specific
           | diagnostics.
           | 
           | At any rate I completely agree with the parent that changing
           | the set throwable exceptions should be considered a breaking
           | API change and should be enforced by the compiler. If an API
           | method wants to future-proof and be able to throw anything,
           | it can declare just that and everybody will know to expect
           | the unexpected.
           | 
           | That's why I vastly prefer Rust's Result<> system which does
           | most of what exceptions do and with fairly similar ergonomics
           | but with normal return values and all the type checking that
           | it involves.
        
             | scarface_74 wrote:
             | > Something relevant to the error condition, probably?
             | 
             | And in every language I know you can have catch blocks for
             | specific errors and then have a generic catch all.
        
               | simias wrote:
               | The ability to catch exceptions is not the issue, it's
               | knowing what to do once they're caught that's the
               | problem.
               | 
               | The parent apparently had exhaustive exception handling,
               | catching all cases. A new exception is now generated,
               | probably signaling a new error condition, you can't
               | expect the calling code to be able to handle it
               | gracefully.
               | 
               | Hence why a compile error might be a better solution
               | here, the coder could decide whether the new exception
               | can fit an existing handler or requires special handling.
        
             | saurik wrote:
             | Realistically speaking, you should essentially never try to
             | "handle" errors with tricky program logic: propagate them
             | up (automatic in languages with exceptions) and eventually
             | --in as few places as possible--report them to the user so
             | the user can decide what to do, not the code.
        
               | simias wrote:
               | I disagree, low level exceptions rarely make sense on
               | their on for the users, the automatic propagation of
               | exceptions just promotes laziness by having a catchall
               | "print(exception); exit(1);" at the top level.
               | 
               | If you're lucky you get a proper stack trace that lets
               | you figure out what's going on, but even that is poor
               | ergonomics.
               | 
               | A high level library should report high level exceptions
               | for instance, not minute details of the lower layers.
               | Nothing is more frustrating that having a program fail
               | and all you have is some cryptic "No such file or
               | directory" or "Operation not permitted" error that
               | doesn't tell you what the code was actually trying to do
               | or give you any hint on how to fix it.
        
           | breadwinner wrote:
           | It is a bad idea according to Microsoft.
           | 
           | See https://learn.microsoft.com/en-
           | us/dotnet/fundamentals/code-a...
        
             | scarface_74 wrote:
             | This is for a library. Whatever the "root" of your code is
             | should have a generic catch all where you do "something",
             | you don't want library code (either yours or the system) to
             | decide for the user what should be done with exceptions.
             | 
             | As a library writer, you should do something about
             | exceptions that you can and rethrow the ones you can't,
        
               | breadwinner wrote:
               | > _Whatever the "root" of your code is should have a
               | generic catch all where you do "something"_
               | 
               | If your only goal is to avoid crashing the app then you
               | can catch the base Exception class at the root of you
               | code and go home. But consider the scenario that you're
               | writing a very important method, where the functionality
               | of your method is very important. If your method does not
               | do its job, the rocket may crash or the patient may die.
               | In this case you want to know what recoverable errors are
               | possible in the functions that your method calls,
               | correct? Wouldn't be it nice to get a guaranteed-by-
               | compiler list of possible, recoverable, errors?
        
               | scarface_74 wrote:
               | And then you have a list of catch blocks with the final
               | one being the base.
        
               | breadwinner wrote:
               | No. You only catch exceptions you can recover from in
               | your method. Other exceptions should be handled in higher
               | layers.
        
               | scarface_74 wrote:
               | I'm talking about the "root" of your app
        
           | revskill wrote:
           | Catching at the root is only half of story. You must also
           | have ability to resume where it was thrown.
        
         | croes wrote:
         | >you would have to catch the root Exception class, which
         | everyone agrees is a bad idea.
         | 
         | It's a bad idea to catch an unexpected exception?
        
           | simias wrote:
           | The issue brought up by the parent is that the exception
           | needed not be unexpected. If the compiler had generated an
           | error because a new exception type could be thrown by the
           | third party API, the developer could have decided that they
           | either couldn't do anything with it and just add a catchall,
           | or actually implement code specifically to handle this issue.
           | 
           | IMO the entire concept of "unexpected exception" is bogus in
           | and of itself. That's like calling "exit(1)" in your code
           | when something goes wrong instead of using proper error
           | handling facilities.
           | 
           | Admittedly it's very convenient for small scripts where
           | proper error handling might be overkill, but for any serious
           | application it's a massive footgun IMO.
        
           | tom_ wrote:
           | You run the risk of swallowing something interesting. This
           | was always the problem with C# exceptions to my mind: there
           | was just one mechanism for reporting errors, which covered
           | everything from the most non-erroneous of events, that aren't
           | even errors, such as file not found or invalid file name
           | encoding or failed to write data to file or socket error
           | during write, to stuff that absolutely should cause the
           | program to go pop and die immediately, such as a null pointer
           | or divide by zero.
           | 
           | Anyway, I sympathize with this thread's OP, as I've had
           | exactly the same problem with C#. In the end, I came to the
           | conclusion you kind of do often have to catch every
           | exception, because you can't trust the functions you call,
           | and the documented exception lists for framework stuff are
           | not always accurate. Safest is to put each call in its own
           | try...catch block, and do any property accesses outside the
           | try...catch block so you find out about the
           | NullReferenceExceptions.
           | 
           | This is annoyingly verbose though.
           | 
           | It also does assume your setters and getters don't throw
           | anything.
           | 
           | I mostly liked C#, but the exceptions aspect is not good.
           | Similarly, I've generally despised working with Go, but the
           | way it deals with errors is not its worst feature! (You do
           | have to pay attention to the linter output though! It's the
           | same err variable every time, so the compiler's fastidious
           | checks for variable use are always foiled...)
        
             | EricRiese wrote:
             | > the most non-erroneous of events, that aren't even
             | errors...
             | 
             | You're applying a value judgement that the language makers
             | can't and shouldn't make for you. Whether the error is
             | innocuous or malicious, the happy path of your code cannot
             | proceed and needs explicit handling by the programmer.
        
               | tom_ wrote:
               | No value judgement here, I don't think, at least not from
               | me (though of course my values inform every opinion I
               | have, including this one). I simply claim that every path
               | is equivalent, and all must be considered. I claim there
               | is no so-called "happy path" - a term I reject - nor do
               | "errors" really exist.
               | 
               | There is one case where the socket send succeeded, and
               | another where it failed. There is one case where the file
               | name encoding is valid, and another where it is not. And
               | so on. It makes no sense, in my view, for one case to be
               | handled with one language mechanism and the other case to
               | be handled with another.
               | 
               | By comparison, there is (or so I claim!) no valid path
               | where a null pointer is dereferenced, or where a value is
               | divided by zero. So I will accept the requirement for
               | some other mechanism for dealing with these - though by
               | the time the code is released to the wild, I would hope
               | that all such occurrences would have been eliminated.
        
               | saurik wrote:
               | The correct behavior for not just the vast majority of
               | these conditions but virtually all of them is the same:
               | propagate the error. You aren't going to sit around and
               | try to "recover" from failing to send a packet or encode
               | a filename: you just report it to higher level code.
               | There _might_ be something you can do at a higher level--
               | such as reconnecting or using a different server from a
               | load balancing list--but at that point the exact _reason_
               | is irrelevant (and yet should be maintained in case the
               | higher level code needs to inform the user in either a
               | dialog or a log file): failure is, virtually all of the
               | time, boolean in nature and a non-local phenomenon.
        
               | tom_ wrote:
               | My opinion was informed by writing the higher levels
               | rather than the lower ones! Yes, the code absolutely _is_
               | going to try to recover from sending a packet, because it
               | was sending that in service of some larger goal that now
               | needs unwinding partway through. And, yes, it is going to
               | try to reconnect, or use a different server - or
               | whatever.
               | 
               | The thing it really doesn't want to do is punt the issue
               | on to some higher level. Because there isn't one.
               | 
               | But, equally, you don't want to be swallowing interesting
               | problems that genuinely indicate actual bugs. Those, you
               | do want to pass on (and due to the lack of any higher
               | level, your process will be killed, and some mechanism
               | will spring into action to produce a report).
        
         | EMM_386 wrote:
         | > Someone made a change in a function I was calling, and it
         | started throwing a new exception.
         | 
         | If it has nothing to do with your code, why not either let it
         | bubble up the stack or try/finally it to do whatever clean-up
         | you need to do before re-throwing it and letting it move on to
         | a global logger or similar.
         | 
         | If it _is_ something you care about you can always catch the
         | specific exception type and handle it.
         | 
         | Maybe whoever worked on the other function wasn't even aware it
         | might throw that particular exception.
         | 
         | Granted I am far more experienced in C# than Java, but the idea
         | that with every code change you potentially have to review and
         | declare every single type of exception seems like madness
         | ("what happens if we pull the cord when it's here, what type
         | will that throw?").
        
           | breadwinner wrote:
           | Imagine that the functionality implemented by your method is
           | very important. This functionality should not be needlessly
           | aborted. If so you want to be aware of errors you can recover
           | from, correct? This is why checked exceptions are helpful. It
           | gives you a guaranteed-by-compiler list of exceptions and you
           | can decide which of those you should recover from.
        
             | throwanem wrote:
             | Or I can catch any exception out of the codepath I'm
             | concerned about, examine it to see if it's one of the known
             | set of exceptions I intend to handle, and then either do so
             | or rethrow it.
             | 
             | If there's a benefit to compile-time exception checking
             | over this method, I have to admit I don't see it. But I've
             | also never worked deeply enough with Java to be familiar
             | with the nuances of its exception handling, so that may be
             | why.
        
               | newaccount74 wrote:
               | The problem is that handling arbitrary errors can be very
               | complex. It's easy to make errors in the error handling
               | code. Especially if the error you are handling never
               | occurs -- because there's no way to test error handling
               | code for an error that never happens.
               | 
               | That's why it is really useful to know when a call can
               | fail, and what kinds of errors you have to expect.
               | 
               | If you always have to add a generic error handler for
               | possible unknown errors, then you'll write a lot of
               | untested, dead code.
        
               | joexner wrote:
               | Checked exceptions give you, essentially, syntactic sugar
               | for handling just a few kinds of exceptions and re-
               | throwing the rest.
               | 
               | It's useful when there are one or two error cases you
               | want to retry or handle specially, but you want to just
               | barf any other error up the stack. It's a specific use
               | case but it's prevalent.
               | 
               | The downside is that sugar can only separate your error
               | conditions by Java type. If everything is just an
               | Exception, you'll have to use sort out your error cases
               | in code.
        
               | throwanem wrote:
               | By the sound of it, that's more like syntactic salt, or
               | maybe syntactic thallium - `else { throw error; }` seems
               | much simpler by comparison.
               | 
               | Being able to know what exceptions can possibly be thrown
               | at a given point is useful, but seems like a problem
               | better solved through static analysis than by requiring
               | annotations.
        
             | Akronymus wrote:
             | This is one of the main reasons why I personally try to
             | avoid exceptions and instead encode failure into the type
             | system. For example, returning a Result<'t,string> rather
             | than just a 't. (You can refine the type further). Altough,
             | in most languages that approach results in a lot of
             | boilerplate.
             | 
             | In F# it works quite well, though.
        
               | necovek wrote:
               | How is this different in practice? Checked exceptions are
               | simply "syntactic sugar" for error types (exception
               | objects with a few special handlers to return them and
               | receive them).
               | 
               | You can write almost exactly the same code with your
               | Result by ignoring the error until the point you would
               | catch it with exceptions.
               | 
               | The only difference is that exceptions bubble up if
               | unhandled, which requires a generic catch to match code
               | ignoring errors otherwise.
        
               | Akronymus wrote:
               | > How is this different in practice?
               | 
               | It is quite a bit more explicit in that something can
               | have an error and in what ways it can error (if you
               | enrich the type in that way), while still being
               | ergonomical. And it reserves exceptions for the truly
               | unexpected/exceptional cases.
        
             | wiseowise wrote:
             | Yes. But it's not as simple as checked=good, unchecked=bad
             | and vice versa.
        
       | mkleczek wrote:
       | Checked exceptions failed in Java because they don't play well
       | with parametric polymorphism. Union types might help (exception
       | list declaration is actually a union type) but I don't think it
       | would succeed anyway as it is not general enough.
       | 
       | Handling effects and effect polymorphism in programming languages
       | is an active area of research and there are some new languages
       | that try to approach the problem (ie. Koka).
       | 
       | Haskell has several effect libraries (effectful, cleff, eff,
       | polysemy) that look quite nice.
       | 
       | Idris with its dependent types allows precise definition of
       | effects in function signatures.
        
         | valenterry wrote:
         | No, that's not it. The same problem is true for other error-
         | handling types, such as the "either" or "result" types.
         | Languages without union types or a similar mechanism are unable
         | to define the error types adhoc and force the developer to
         | define them in advance, which is very unergonomic.
         | 
         | But that is true in both cases. The problems with checked
         | exceptions come on top, namely that you cannot use all the
         | regular value-machinery to transform and manipulate the result
         | of a function-call.
        
           | mkleczek wrote:
           | > No, that's not it. The same problem is true for other
           | error-handling types, such as the "either" or "result" types.
           | Languages without union types or a similar mechanism are
           | unable to define the error types adhoc and force the
           | developer to define them in advance, which is very
           | unergonomic
           | 
           | We are saying the same thing, aren't we?
           | 
           | But it is not only about exceptions. Other effects have the
           | same problem and you need a mechanism to define effect
           | polymorphic functions.
        
             | valenterry wrote:
             | Maybe we do and I can't read. :-)
        
       | DamonHD wrote:
       | "Nobody uses [checked exceptions" in Java, this casually claims.
       | 
       | Oh dear. I have a slew of current counter examples on my machine,
       | in GitHub, etc.
       | 
       | Either carelessly untrue or else annoying hypebole.
       | 
       | Made the rest very hard for me to read.
        
       | he0001 wrote:
       | Checked exceptions are great. But they were abandoned due to the
       | functional paradigm so now it's cumbersome to use them. Errors
       | should be first class of any language and not bolted on as some
       | sort of afterthought, language or design. Errors are something
       | that occurs in real life and should be handled as such. Unchecked
       | errors are a pain to deal with and always are unexpected which
       | you need to dig into the code to see if they are thrown. This is
       | a huge issue for me. I believe that Java should and could make
       | them more easier to use by adding language support to transform
       | them.
        
         | masklinn wrote:
         | They were abandoned long before "the functional paradigm". They
         | started being a problem when the Java community decided
         | interactions should go through interfaces over concrete types,
         | and it only got worse with genetics then functional streams.
        
           | he0001 wrote:
           | Not sure about that. Reinhold said so explicitly during a
           | conference when they launched Java 8. I don't see interfaces
           | over concrete types as an issue here.
        
             | masklinn wrote:
             | That checked exceptions are an issue with interfaces is
             | literally what TFA is about.
        
               | he0001 wrote:
               | Yeah, and he's wrong about it.
        
       | cubefox wrote:
       | While I broadly agree with this, the author doesn't appear to go
       | far enough himself.
       | 
       | > Functional error handling, using Option and Result types, is
       | rapidly becoming the standard operating procedure in essentially
       | every language, because it relies on nothing but values and
       | types. They are more of the same, and so they fit right into the
       | existing language machinery.
       | 
       | I agree that Optional types are better than checked exceptions,
       | but they are worse than union types. A function with the union
       | return type String|Error (read: String or Error) is allowed to
       | return another function with return type String. Similarly, a
       | function which accepts the type String|Error as a parameter also
       | accepts parameters of type String. With Option types this doesn't
       | work. Option<String> and String are incompatible types, so code
       | has to be rewritten if the types change.
       | 
       | Personally, I would go so far as to say that union types should
       | replace exceptions in general, checked or unchecked, as well as
       | any implicit nullabiliy, which can be replaced with the explicit
       | union type Foo|Null.
       | 
       | (Although some special syntax for handling Foo|Exception or
       | Foo|Null is probably a good idea, as "error" and "nothing" are
       | pretty general categories.)
        
         | Skeime wrote:
         | I often run into the opposite problem: There are union types,
         | but I would really like sum types (i.e. tagged unions). The
         | common case is a data structure where I cannot guarantee that
         | the user will not want to use Null as a value in my data
         | structure, but I also need to represent the absence of values
         | myself. With a sum type, my absence would be None and the
         | user's absence would be Some(None). With union types, I need to
         | somehow create an extra sentinel value myself.
        
           | cubefox wrote:
           | Something like String|NoAnswer|None doesn't sound too bad to
           | me here...
        
             | Skeime wrote:
             | Except that NoAnswer has none of the language's (and it's
             | library's) support for the built-in None. And you might run
             | into problems when trying nest the data structure, which
             | might be a reasonable thing for your users to do.
        
             | DougBTX wrote:
             | This isn't just a problem with custom types, it affects
             | generic iteration types too. With a next() -> Option<T>
             | method, an Iterator can iterate over any type of element.
        
               | cubefox wrote:
               | Yeah, but there could be a type T for "any type"
               | additional to union types, basically like TRUE in Boolean
               | logic. (Theoretically you could also have F for "no
               | type", intersection types (AND), negation types (not
               | Foo), and any logical complex of those.)
        
         | Sinidir wrote:
         | Yeah Containers like Option<T> and Result<T> not having a
         | proper subtyping relation to T is major flaw. I mean you are
         | basically giving a stronger guarantee if you are returning T
         | instead of T or an Error, yet you break every callsite.
         | 
         | I have been thinking about this in terms of a data oriented
         | programming language like clojure, but having types/schemas. If
         | you use Union Types for something like getting a value out of a
         | map for a specific key, then if you knew the schema of a
         | specific map you could reduce the return type from {T, Error}
         | to just the type of the value T that you know is there.
         | 
         | Basically a sufficiently smart compiler with the necessary
         | information could make you not have to deal with errors at all
         | in certain cases. With Result/Option/Maybe this would not be
         | possible. It would always infect the entire callstack and you
         | would always have to deal with it.
        
         | Kwpolska wrote:
         | If your language supports implicit user-defined conversions
         | (like C#), you could `return "foo";` from a method returning
         | `Result<string>`.
        
       | o1y32 wrote:
       | This is a very old topic. Insights from Anders Hejlsberg:
       | 
       | https://www.artima.com/articles/the-trouble-with-checked-exc...
        
         | hodgesrm wrote:
         | Great article with a lot of pithy insights. Here's one of the
         | core issues with checked exceptions. 2nd sentence to the last
         | is the point.
         | 
         | > Anders Hejlsberg: Yeah, well, Einstein said that, "Do the
         | simplest thing possible, but no simpler." The concern I have
         | about checked exceptions is the handcuffs they put on
         | programmers. You see programmers picking up new APIs that have
         | all these throws clauses, and then you see how convoluted their
         | code gets, and you realize the checked exceptions aren't
         | helping them any. It is sort of these dictatorial API designers
         | telling you how to do your exception handling. They should not
         | be doing that.
        
       | hota_mazi wrote:
       | Checked exceptions failed because developers are lazy and don't
       | want to check for errors.
       | 
       | Checked exceptions force you to do more work, but it's essential
       | work to make your code more robust.
       | 
       | Replacing them with runtime exceptions gives you the illusion of
       | clearer code, but all you have is actually code that is more
       | likely to crash.
        
         | jdm2212 wrote:
         | It's not a matter of laziness. Most of the time there's just
         | nothing you can do.
         | 
         | Like if you try to construct a URI based on a configured URI
         | string, and get URISyntaxException... wtf should you even do?
         | You can't recover, because your code shouldn't be mutating
         | config. Similarly with basically any JSON parsing exception.
         | And most disk read/write exceptions.
         | 
         | And in modern applications the caller is probably on the other
         | side of an RPC call, and checked exceptions won't propagate
         | sanely across the RPC anyway.
        
       | redeeman wrote:
       | > Can I specify that implementations of foo can throw no, some,
       | or all exceptions?
       | 
       | just because theres the possibility of a throw in the signature,
       | does not mean it has to be done in a concrete implementation, and
       | if implementations are really to be exchangable, it has to be
       | handled in case implementation changes.
       | 
       | > What would it even mean to write something like throws *?
       | 
       | Roughly similar to "throws Exception" or "throws Throwable" -
       | that it may throw whatever you have under the sun?
       | 
       | > Concretely, checked exceptions in Java failed because Java
       | lacks "throwingness polymorphism", if you will.
       | 
       | it does allow for extending exceptions, so an interface can
       | define some exceptions that are basic, and implementations can
       | then choose to extend those to provide MORE detail if needed.
       | 
       | > Was Java wrong to add checked exceptions? No. They took a risk
       | and it didn't pay off.
       | 
       | Uhm.... Im gonna go ahead and use some other opinion on this
        
         | vmaurin wrote:
         | For checked exception, as you mention, "throws *" is throws
         | Exception, then people can refine it on implementation.
         | 
         | As many language feature, checked exception are often misused
         | and/or misunderstood. Too many time I have seen people blindly
         | propagating checked exception above, and you end up having a
         | controller method exposing a SQLException. Then for sure, it is
         | better to use unchecked exceptions overall
        
       | EricRiese wrote:
       | Java has a lot of "throwingness polymorphism". You can extend an
       | interface to say a method throws a subclass of the Exception
       | thrown by the parent interface method. You can extend an
       | interface and say that that method actually doesn't throw an
       | exception at all.                 interface
       | NonThrowingAutoCloseable extends AutoCloseable {         void
       | close();       }
       | 
       | You can also declare that you throw a generic exception type
       | interface ThrowingSupplier<E extends Exception> {         void
       | get() throws E;       }
        
       | pshirshov wrote:
       | The sad truth is: it's pointless to talk about that with those
       | who are not used to structures like Either/Result/IO monads. And
       | those who are already know why and don't need such articles.
        
       | jacquesm wrote:
       | Erlang does this the right way: the process fails (the Erlang
       | sub-process) but the super visor tree anticipates such failures
       | and will restart the process that failed which if the rest of the
       | application is properly designed will handle the error without
       | loss of consistency. Depending on the failure it can also
       | escalate further up to deal with larger and larger levels of
       | malfunctions. Properly implemented short of gross hardware
       | failure (all nodes down) this will result in a system that is
       | always at or near the maximum possible availability as permitted
       | by the hardware.
        
       | jdm2212 wrote:
       | Checked exceptions failed because 99 times out of 100 the
       | exception is not recoverable, so the try/catch block is just
       | wasting everyone's time. (In 7 years as a Java dev I can think of
       | one time I wrote code that tried to recover from IOException
       | instead of just making the caller retry.)
       | 
       | Even when the exceptions is theoretically recoverable, it has to
       | get propagated up properly to the caller who should be handling
       | recovery from it. But checked exceptions don't propagate sanely
       | through executors and across RPC calls, so good luck with that.
        
         | Gibbon1 wrote:
         | I always feel like the error domain changes as you go across
         | program domains.
         | 
         | I keep returning to something about error handling that bothers
         | me. People argue what the proper way to handle errors is
         | without really considering that it's highly context sensitive.
         | Which makes me think you should be able to pass an error
         | handler down to lower level functions that tells them what to
         | do when something bad happens. Sort of like recent ideas where
         | you pass functions a allocator instead of them calling malloc()
         | directly or whatever.
        
         | ratherbefuddled wrote:
         | Though I mostly agree it's slightly more subtle IMO. They
         | failed because the writer of the method cannot know what the
         | caller can recover from but must decide before compile time.
         | The caller gets little say.
        
         | treffer wrote:
         | I find the rust `?` construct nice for this: give me the
         | success result or propagate the error. It is based on a result
         | type though.
         | 
         | The try/catch construct require too much code for the common
         | propagate case.
         | 
         | It would be nice if Java had a similar construct for error
         | handling.
        
           | amalcon wrote:
           | "throws IOException" is too much code? Or is the issue more
           | that you can't really do autocoercion to a declared thrown
           | type in Java the same way that you can do in Rust?
           | 
           | Proliferation of types is an issue in Java, but the whole
           | language has that problem. It's not just exceptions.
        
           | jayd16 wrote:
           | The Task type in C# with the await unwrap sugar is similar to
           | this. You can even check for and grab the exception without
           | throwing if you want to.
           | 
           | Still, it's too bad the error type must be a throwable. I
           | kind of wish it could just be a plain type so you can error
           | or cancel without generating stack traces. Awaiting a failed
           | task could still throw.
           | 
           | Would be a nice perf boost. As it is now, you don't want to
           | actually cancel or fail a C# task in performance critical
           | code. You need to successfully complete the Task and return
           | an error, which is pretty confusing.
        
         | jayd16 wrote:
         | Is this a problem with checked exceptions or a problem with
         | Java? Couldn't exceptions be treated as robustly as param and
         | return types in the generic system so they are more composable?
         | Why not a Future<V, E1> interface?
         | 
         | The argument that some exceptions will always be runtime (like
         | oom) so checked exceptions are flawed is harder to argue
         | against but I would also say it's a matter of opinion whether
         | you feel like it's worth dropping entirely.
        
       | rcme wrote:
       | The issue with all exceptions, and error handling in general, is
       | that only very rarely is there actually recourse for an error.
       | For instance, in an HTTP request handler, the vast majority of
       | errors will end up as something like a 500. Checked exceptions
       | are annoying because they make you explicitly handle an error
       | when you likely already have a handler in place to handle _all_
       | exceptions, checked or otherwise.
        
         | inglor wrote:
         | Exactly, the boilerplate overhead isn't worth it when 95% of
         | times you don't have anything meaningful to do with the
         | exception but let it propagate.
        
         | computerdork wrote:
         | To me, you kind of hit _both_ the pros and cons of checked
         | exceptions. It makes the calling code have to deal with an
         | important error case. This is actually super useful for very
         | important error situations (for instance, let 's say you're
         | writing some type of file-processing class, and the class was
         | unable to open the file for some reason, it might be a good
         | situation to throw a checked exception as this can probably
         | happen reasonably often).
         | 
         | To me, the difficulty with exceptions is when writing a method
         | that needs to throw an exception is deciding whether it should
         | throw one that is actually checked. In fact, read a few of
         | years ago most exceptions should actually be _unchecked,_ but
         | at least then, this was still greatly up for debate.
         | 
         | ... as I type this, am realizing having the option of unchecked
         | and checked exceptions is nice in that they reduce the amount
         | error code you have to write. Because by throwing unchecked
         | exceptions, this is error handling that all the entire calling
         | code-chain doesn 't need to deal with. The obvious situations
         | are system errors like out of memory errors, missing
         | resources... but also those programmatic errors that are
         | unexpected, like array out of bounds, ClassCastException.
         | 
         | Also, the calling chain then has the option to handle it if
         | it's needed. For instance, if you're writing a low-level
         | messaging queue that needs to report system errors (btw, this
         | was a real situation that happened to me). Just my two cents.
        
       ___________________________________________________________________
       (page generated 2023-07-15 23:01 UTC)