5 ms·
Why? I never understood the objection to checked exceptions. It seems to me they would give me exactly what I want! (Granted, I'm a Haskell programmer, not a
by tome 2y ago
Why? I never understood the objection to checked exceptions. It seems to me they would give me exactly what I want! (Granted, I'm a Haskell programmer, not a Java programmer.)
- demurgos 2y agoOne of the problems is that return types and exception types are not unified so it makes composition harder. `Result` brings exceptions into the regular type system
- nine_k 2y agoWithout parametric types, your methods grew longer and longer lists in the "throws" clause. It was easy to miss one of the dozen exceptions and only have the compiler point you at it after wasting several minutes on compilation (think 133MHz CPUs and slow HDDs). Type inference in modern Java makes you forget how long and repetitive type signatures used to be in it 20 years ago. BTW this makes Result<D, E> more ergonomic anyway: you still have only one E, at least one per invocation level. With checked exceptions, you had to haul a whole garland.
- michaelcampbell 2y ago> It was easy to miss one of the dozen exceptions and only have the compiler point you at it This sounds like (one of?) the killer feature of types, though. > after wasting several minutes on compilation (think 133MHz CPUs and slow HDDs). Sure, but everything we did was slow then.
- robertlagrant 2y agoI don't remember Java being that slow even on a 133MHz CPU. Maybe if it was a giant project, though.
- nine_k 2y agoA large project, and a poor ant config %) Also, small RAM. Incremental changes were pretty fast though. (Then IDEA came.)
- BlarfMcFlarf 2y agoWeak type system made it incompatible with function types in practice, and poor compositional tools made it hard to deal with. In Rust, you have entire libraries around the ergonomics and management of errors to match different applications. In Java, you are stuck using an error management strategy dictated by the error type itself.
- kaba0 2y agoJava is perfectly capable of expressing this thought, it has sum types, so the Result-type road is absolutely available to it. (Optional<T>=Maybe T is part of the JDK even) Although, it might not optimize as well, as this is not common practice.
- tome 2y agoThanks to everyone who replied. I'm responding to my top-level comment rather than to everyone individually. It sounds like the objection is not to the fact that the exception type is checked, but that "checkedness" doesn't fit in well with other aspects of the language. For example, you can't be polymorphic over it, or abstract over it.
- ragnese 2y agoI'll let you in on my cynical take. Warning: it's both cynical and a bit condescending. The truth is that programmers are just as susceptible to fads, cargo cults, and appeals to authority as anyone else. Around the same time that everyone decided that checked exceptions were horrible, programmers were also deciding that they didn't like static typing at all and a great many were jumping to Python and JavaScript for everything. Now, the pendulum has swung the other way, and everyone likes static typing, and they'll even go so far as to proclaim their love for Rust's Result type, which is the same damn thing as checked exceptions in practice (with extremely similar pros and cons and ergonomics). But, they haven't yet gotten around to questioning their belief that checked exceptions are axiomatically bad, so you'll see people point out every minute, trivial, difference between Java's checked exceptions and Rust's Result type in an attempt to rationalize why they like one and not the other. Some will also claim that Rust doesn't have unchecked exceptions, just because you can set a compile-time option to abort on what Rust calls "panics".
- taeric 2y agoMy take is more that functional style really wants everything to be a value. And checked exceptions break that pretty heavily. Same reason most functional style heavy languages don't have statements as much as they do expressions. For places it gets really intrusive, consider that when/where an exception is raised in a map statement. There is also the annoyance of how junior programmers will fixate on trying to model every exception thinking that means they have handled them. With many exceptions being necessarily delivered all the way to the user, granular modeling of them is of rapidly diminishing value compared to quickly getting it to the user in the first place.
- tsimionescu 2y agoEspecially in more modern Java, a huge problem of checked exceptions is that they are not parametrizable. So you can't (easily) call map(list, foo) if foo throws an unchecked exception, since map() doesn't throw, and there is no way to make map() throw conditionally.
- Groxx 2y agoThis is the vast majority of the problem. As a consequence, APIs with callbacks or wrapping generic things of any kind are forced to choose between "throws Exception" and a runtime exception, losing all detail and possibly choosing the opposite of what is actually needed. And there are TONS of these, and they're heavily used. It's a badly crippled implementation that has poisoned the well, the concept is just fine. People like Result<T,E> well enough, and that's exactly as expressive as checked exceptions in languages that implement them correctly.
- dllthomas 2y ago> forced to choose between "throws Exception" and a runtime exception, Not that it fully defangs the issue, but there are two other options that come to mind. 1) suppress the exception (maybe with a log) 2) capture the fact (and maybe content) of the exception and expose it through the API in some other way Probably neither of these is going to be appropriate for most cases, but seem worth surfacing because 1 is often chosen even when it's inappropriate and 2 is often overlooked as an option.
- _ZeD_ 2y agowhile `Result<T,E>` is very similar to an exception there are a couple of limitations: 1. results must be handled explicitly, exceptions are forwarded automatically. This means I must deal with each result locally, even with a "forward up" to the caller. This means much complex and less readable code 2. exceptions have a stacktrace, handled automatically for you. This means more precise location of where the error occurred. For the same reasons exceptions are heavier - as they keep more data AND is updated for each frame (when handled, and depends on the implementations) 3. the syntax to deal with exceptions is ugly as hell. I haven't found anything better than the python syntax, and still is not something you like to see.
- teaearlgraycold 2y agoNot all exceptions are checked. This kind of ruins the whole point. IllegalArgumentException for example is very common and would be what you’d use for a bad input to something like new URL(). So it’s a very common exception to see, maybe second only to the NPE, and yet it won’t be checked. What the fuck is the point then?
- tikhonj 2y agoChecked exceptions in Java are not first-class as values or types. They don't behave like anything else in the language and you can't meaningfully abstract over them. Combine this with a bunch of boilerplate needed to handle them and you have unescapably painful APIs.
- karatinversion 2y agoOn top of all the other problems pointed out, java's checked exceptions don't even do a good job of indicating possible failure conditions. The standard library set the tone on this with exception like IOException, which has an enormous amount of subclasses represting different IOExceptions, and is thrown by anything related to IO. So I still need to rely on documenation or code inspection to understand what exceptions might actually be thrown and what they mean, if I want to recover from errors. The classic case of this is that the compiler cannot tell that new StringReader("Example").read() doesn't throw an SSLException.
- ragnese 2y agoBut, you're conflating a language feature "Java's checked exceptions" with bad API design, which is obviously possible with any combination of any language's features. I agree that Java's standard library has a ton of bad APIs--not just the checked exception hierarchies, but that does not necessarily mean that checked exceptions are a bad idea. Just to put an extremely fine point on it, one thing I always complain about is the JDBC ResultSet API. If I have a SQL database table that has a nullable integer column, and I query that column for a row that happens to have that column set to `NULL`, then the ResultSet API will give me the int value `0` for that column and I have to just know that I need to call `wasNull()` (or whatever it's called) to ask the ResultSet if the last thing it returned was really supposed to be null or not. Yet, the ResultSet API does not make me think that Java shouldn't have `int`, or class methods, or any way to query databases--it only makes me think that the JDBC API is really, really, poor.
- nine_k 2y agoThe question was, why checked exceptions saw no love. A big part of the answer is that the stdlib did a very poor job showcasing them. No wonder, it was an attempt to graft an FP-esque feature onto a deeply imperative OOP language, with a development team aligned with OOP concepts. I suspect the stdlib team lacked either the understanding or the means to make the use of e.g. IOException nicer. They didn't even have type parameters at their disposal yet. It was botched as a result, and deprecated. It's an interesting mental exercise to try and design a better implementation of the idea within the constraints of Java. (Implementing the monadic approach is likely the easiest and cleanest way.)
- wvenable 2y ago> I never understood the objection to checked exceptions. Any non-trivial code tree will throw an unmanageable number of exceptions. The solution, in Java, is to catch exceptions and rethrow them as a new exception with the previous exception squirrelled away untyped within it. So either way, it's a failure. It also doesn't work with higher-order functions. It's also, in my opinion, conflicts with polymorphism. If you have a interface that can be implemented many different ways, you can't possibly have correctly typed checked exceptions for it.
- treflop 2y agoDisagree. Semantically if you are wrapping an error it means you that you are purposely shifting the responsibility and declaring that your function is responsible for the failure. If you instead re-throw the error, such as if was an IOException, semantically it means that you are not responsible for the error and that the calling code should deal it. If later on you need to add a new thrown exception, it does result in a breaking change on upgrade but it should, as new errors should be thoughtfully handled. The real culprit is that most people realize that they are not building rockets and thus think that errors, recovery from error and mitigating data loss are not worth their time. Just because I’m not writing a rocket schematic does not mean it’s okay that you lose my 2 hours of work, or that PagerDuty alerts go off at 2am because there was one blip and the database is now slightly corrupted, or you put my account into an undefined state where I have to beg support for help. If errors are checked, many people just wrap all errors will-nilly. If errors are not checked, they never bother with sensible error handling. To me, whether a language has checked errors or not is just feature of the tool, and whether someone uses tools correctly or not is up to the user.
- wvenable 2y ago> Semantically if you are wrapping an error it means you that you are purposely shifting the responsibility and declaring that your function is responsible for the failure. I don't understand what this means. If you wrap an exception and throw it, how has your function become responsible for the failure? > If errors are not checked, they never bother with sensible error handling. My most robust desktop application has a single exception handler at the event loop that puts the exception message in a messagebox. Try and save your file to a network location that doesn't exist anymore? Click OK and try again. > To me, whether a language has checked errors or not is just feature of the tool You didn't address the criticism that checked exceptions are fundamentally incompatible with other features such as higher order functions and polymorphism. It's an experiment that has ultimately failed in practice.