6 ms·
It doesn't help that there's no way to specify in the contract of the function that it can throw. Blowing up your entire program because it can't parse a URL is
by cageface 2y ago
It doesn't help that there's no way to specify in the contract of the function that it can throw. Blowing up your entire program because it can't parse a URL is something you should be made explicitly aware of.
- mkleczek 2y agoAhh... MalformedURLException is checked in Java and is a source of constant backlash from developers - for over 20 years :)
- nine_k 2y agoChecked exceptions have been tried in Java, and were universally hated, mostly for poor ergonomics. A very similar thing, the Result type in Rust, does pretty well. (Much like Maybe / Either do in Haskell.)
- tome 2y agoWhy? 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.
- kaba0 2y agoChecked exceptions are only hated because java historically hasn’t provided enough syntactic sugar for it, basing it on inheritance is not the best decision and due to it being sort of outside java’s general type system. (Note: Java is thinking about making switch expressions able to handle exception cases as well, which would help tremendously) I think the backlash against them was way too hard to the point that basically no other mainstream language dared trying them out, even though exceptions in general are - in my slightly subjective opinion - the best form of error handling, paired with some static guarantees sounds like an optimum. They do the correct thing by default (auto-unwrap in the happy case, auto-bubble up with a stacktrace in the unhappy one), and let’s one control their handling on as wide or narrow scope as required (try blocks). Nonetheless, I think that they are to be used for exceptional situations, like network interrupts, OS errors, etc. Expected error conditions, like parsing a number failing is best encoded as a sum type, analogously to an Either/Result type. Newer research languages with effect types might resurface this “lost” idea, I’m definitely looking forward to them.
- jondwillis 2y agoSwift does this well. People who object to checked errors probably don’t have a good working mental model of error handling, and just want to write lower-quality code that happens to work. Ergonomics are secondary to that in all cases, IMO.
- ruszki 2y agoNo. It’s hated by the majority because it shows a problem which should be handled, but most developers don’t want to care about exception handling, and because of this they make terrible code. They would make bad code even without checked exceptions. Checked exceptions just make it obvious. However, when high profile people criticise it, they criticise it because, for example, interfaces work terribly with checked exceptions. There are problems with collections for example. Like when filesystem backs a list, then that list cannot throw IOException. This is why there is RuntimeIOException now.
- hansvm 2y agoZig's exception handling is also, similarly, quite nice.
- watwut 2y agoThey are hated by some vocal people, who do not like that they have to actually deal with exceptions rather then ignoring them, fingers crossed it wont happen.
- Joker_vD 2y agoThere is also no way to specify in the contract of the function that it can return null. Blowing up your entire program because it can't parse a URL and returns you a booby-trapped value that blows up your program when you try to use it as an URL is something you should be made explicitly aware of. Right? Although there are docs, of course, but who has time to read them? Or write them, for that matter? For some reason many client programmers are perfectly fine with calling random functions that has names sounding close enough and then expecting those functions to behave the way they expect them to behave, and many library programmers don't bother to document the actual behaviour of their functions because they for some reason believe that descriptive and self-evident (well, self-evident to them) names are enough of documentation. And you know, I think I can safely bet $5 the way TFA came to be was something like this: the author wrote a code that used URL() with no error recovery-path whatsoever, ran it, and it blew up on that line. The author then rolled its eyes, wrote an (admittedly) ugly try-catch around, and went to write the article complaining about exception. Now, I can bet another $5 that if URL() returned null what would happen instead would be that the author would write a code that used URL() with no error recovery-path whatsoever, would run it, and it would blow up somewhere down the line, on call to fetch() or something. The author would then ponder the code for several minutes, trying to figure out what exactly fetch() didn't like about the URL it was provided with, find that null came from the call to URL() and add the error recovery there. Crucially, they later would not go and write a article complaining about null returns because they regard it as a completely normal and acceptable behaviour.
- merrywhether 2y agoThis is one of the reasons for the broad popularity of TypeScript, where “[specifying] in the contract of the function that it can return null” is foundational to the experience. Meanwhile thrown errors are unmodelable COMEFROM statements in TS and many other languages.