8 ms·
> Java is a great language because it's boring [...] Types are assertions we make about the world This is less of a mind-was-changed case and more just controv
by Terr_ 2y ago
> Java is a great language because it's boring [...] Types are assertions we make about the world
This is less of a mind-was-changed case and more just controversial, but... Checked Exceptions were a fundamentally good idea. They just needed some syntactic sugar to help redirect certain developers into less self-destructive ways of procrastinating on proper error handling.
In brief for non-Java folks: Checked Exceptions are a subset of all Exceptions. To throw them, they must be part of the function's type signature. To call that function, the caller code must make some kind of decision about what to do when that Checked Exception arrives. [0] It's basically another return type for the method, married with the conventions and flow-control features of Exceptions.
[0] Ex: Let it bubble up unimpeded, adding it to your own function signature; catch it and wrap it in your own exception with a type more appropriate to the layer of abstraction; catch it and log it; catch it and ignore it... Alas, many caught it and wrapped it in a generic RuntimeException.
- dustingetz 2y agoremind me why they don’t work? because “throws Exception” propagates virally to every method in the codebase?
- rad_gruchalski 2y agoWell, if you don’t handle them then… yes? What else can be done?
- karatinversion 2y agoThere's also the fact that common methods threw exception types that were not final, and in fact overly generic. If I call a method that declares itself to throw NoSuchFileException or DirectoryNotEmptyException, I can have a pretty good idea what I might do about it. If it throws IOException without elaboration, on the other hand...
- jkrejcha 2y agoWith regards to I/O, there are generally any number of weird errors you can run into, file not found, directory exists, host unreachable, permission denied, file corrupt, etc etc. Like anything file related will be able to throw any of those exceptions at almost any point in time. I think IOException (or maybe FileSystemException) is probably the best you can do in a lot of I/O cases unless you can dedicate time to handling each of those specially (and there's often not a lot much more you can do except for saying "Access is denied" or "File not found" to the user or by logging it somewhere).
- bluGill 2y agoThere are fatal IO errors and non-fatal. Most of us don't care about the non-fatal case, but mainframes have the concept of "file isn't available now, load tape 12345", "file is being restored from backup, try again tomorrow" - things that your code could handle (if nothing else you should inform the user that this will take a while). There is also the "read off the end of the file" exception which in some languages is the idiomatic way to tell when you have read the whole file. But most IO errors are fatal. It doesn't matter if the filename is not found, or the controller had too many errors talking to the drive and gave up - either way your code can do nothing.
- lmm 2y agoBecause you can't capture the evaluation of a function as a value, or write the type of it. E.g. try to write a generic function that takes a list and a callback, and applies the callback to every element of the list. Now what happens if your callback throws a checked exception? It doesn't work and there's no way to make it work, you just have to write another overload of your function and copy/paste your code. Now what happens if your callback throws two checked exceptions? It doesn't work and there's no way to make it work, you just have to write another overload of your function and copy/paste your code. And you'll never guess what happens if your callback throws three checked exceptions!
- dustingetz 2y agowhat is "it doesn't work" ? The exception is part of the type, so it doesn't typecheck unless all callbacks are of type "... throws Exception"? What's the problem with that? It's not generic enough, i.e. the problem is Java generics are too weak to write something like "throws <T extends Exception>"? (Forgive me, it's been 13 years since I wrote java and only briefly, the questions are earnest) edit, so like `@throws[T <: Exception] def effect[T](): Unit` or something, how is it supposed to work?
- lmm 2y agoYou can't be polymorphic between not throwing and throwing, or between throwing different numbers of exceptions. You have to write something like: <R> List<R> map(Function<? super T,? extends R> mapper) { ... } <R, E1 extends Throwable> List<R> map(FunctionThrows1<? super T,? extends R, E1> mapper) throws E1 { ... } <R, E1 extends Throwable, E2 extends Throwable> List<R> map(FunctionThrows2<? super T,? extends R, E1, E2> mapper) throws E1, E2 { ... } and so on until you get bored.
- catlifeonmars 2y agoAh because there is no way to express E1 | E2 as a type parameter?
- 2y ago
- bluGill 2y agoBecause eventually someone will want to add another exception to a new leaf class and the proper place to handle it is 20 functions down the call tree and every single one of those 20 functions needs to now add that new exception to their signature even though only the handler function cares and you adjusted it. I haven't done java in decades, but I imagine this would get really nasty if you are passing a callback to a third party library and now your callback throws the new exception. Checked exceptions seem like a great idea, but what java did is wrong. I'm not sure if other implementations are better.
- zaphar 2y agoThe counter point here is that at least with Checked Exceptions you know only those 20 functions are part of the code path that can throw that exception. In the runtime exception case you are unaware about that 21'st function elsewhere that now throws it and it's not in the correct handling path anymore. You have no way to assert that the error is always correctly handled in the codebase. You are basically crossing your fingers and hoping that over the life of the codebase you don't break the invariant. What was missing was a good way to convert checked exceptions from code you don't own into your own error domain. So instead java devs just avoided them because it was more work to do the proper error domain modeling.
- bluGill 2y agoLike I said, the implementation is wrong. Adding an exception to that 21st function, and then that whole call chain as well ends up being a lot of work. Sure you eventually find the place to handle it, but it was a lot of effort in the mean time. It gets worse. Sometimes we can prove that the 21st function because of the way it is calling your function can never trigger that exception, but still it will need code to handle it. If that handler code if the 21st function changes to trigger the exception now should be propagated back down but since you handled the exception before checked exceptions won't tell you that you handled it in the wrong place. I don't know how to implement checked exceptions right. On paper they have a lot of great arguments for them. However in practice they don't work well in large projects (at least for java)
- pjc50 2y agoIt was botched from the start because there's so many opportunities for unchecked exceptions as well. Without a more sophisticated type system that represented nullability, you can get NullPointerException anywhere. Divide by zero. And so on. You also have a problem similar to "monads and logging": if you want to log from anywhere in your program, your logging function needs to be exception-tight and deal with all the possible problems such as running out of disk space, otherwise you have to add those everywhere.
- jeroenhd 2y agoUnchecked exceptions are just Java's weird way of what languages call panicking these days. They suck, but as long as you don't throw them yourself and catch them in a logical place (i.e. at request level so your web server doesn't die, at queue level so your data processing management doesn't lock up, etc.) you can usually pretty much ignore them. The worst part about them is that for some reason even standard library methods will throw them. Like when you try `list.Add(1)` on a list without checking if said list is read-only. The overhead of having to read every single bit of documentation in the standard library just to be ahead of panicking standard methods is infuriating. That's got nothing to do with the concept of checked/unchecked exceptions, though, that's just Java's mediocre standard library.
- deleted 2y ago[deleted]
- corytheboyd 2y ago> Without a more sophisticated type system that represented nullability, you can get NullPointerException anywhere. I started working in Java a few months ago and holy shit does this stick out like a sore thumb. Null checks cascade down from gods domain all the way to hell. But oop we missed one here and caused an outage lol add one more! So much wasted human effort around NPE, and yet, we sit around in a weekly meeting getting yelled at about how stability needs to be taken more seriously. Hrm.
- pjc50 2y ago
- flir 2y ago> Alas, many caught it and wrapped it in a generic RuntimeException. Actually sounded great right up until this point. Deal with it, or explicitly acknowledge that you do not. It's honest. Apparently other developers are why we can't have nice things.
- mrkeen 2y agoIt's already very unlikely that you can 'recover-and-proceed' in the context of any business app exception (Security violation, customer not found, no such payment, etc.). So what's left in exception handling is logging and/or rethrowing. And the 'nasty hackish way' of doing it (RuntimeException) already passes a complete stack trace up to the caller.
- wavemode 2y ago"recover and proceed" generally means "log the error and then continue processing on other data, rather than exiting entirely" Lots of different kinds of software tends to follow this pattern - web servers, data pipelines, compilers/build tools, etc.
- pjmlp 2y agoI agree, although I would like to point out Java usually gets the blame for what was actually an idea being done in CLU, Mesa, Modula-3 and C++, before Oak came to be and turned into Java. Additionally, the way result types work, isn't much different, from type system theory point of view. I really miss them in .NET projects, because no one reads method documentation, or bothers to have catch all clauses, and then little fellow crashes in production.
- specialist 2y agoAnti checked exception sentiment is the result of cognitive dissonance from the use of flow-of-control obfuscation frameworks. IoC, DI, aspects, annotations, ORMs, whatever Spring is, runtime code generation, etc. The rationale is something like "runtime metaprogramming would be really terrific, were it not for all those pesky checked exceptions".
- girvo 2y agoChecked exceptions as an idea are great (Nim’s usage of something similar is excellent) but yeah Javas particular implementation was annoying and easy to avoid, so most did.
- baq 2y ago> adding it to your own function signature This is precisely why they are so bad: checked exceptions must not be allowed to be used outside the package (or jar, or whatever, just limit it) otherwise they cause non-local build failures in all dependencies. They're fine if you are developing the artifact that's going to be deployed.
- catlifeonmars 2y agoI think that’s more of a problem of changing the function signature. Not specific to checked exceptions.
- baq 2y agoWhat's specific in checked exceptions is that if you don't handle or silently ignore the new exception, you must change the signature. Then your callers must do the same thing. Then their callers etc. sometimes right down to your public static void main.
- catlifeonmars 2y agoYep, don’t change the function signature of depended on code. That’s the problem :) It would be equally problematic to change the method name, or the argument arity or types (ignoring overloading for the moment). It would be even more problematic if I kept the same function signature, but changed the meaning of the parameters. Then your breakage is silent.
- baq 2y agoyes exactly, hence unchecked exceptions are the sane option.
- catlifeonmars 2y agoDoesn’t that just introduce a silent breaking change to your downstream consumers? That sounds worse. It sounds ok from a library authors perspective but definitely not from a library consumer perspective. A sane thing to do would be to do a version bump.
- kaapipo 2y agoNah, I think having a monadic Maybe/Option type and first-class support for it is the correct solution. Exceptions are fundamentally flawed.
- rixed 2y ago"None" to represent any type of failure sounds similarly fundamentally flawed too.
- lmm 2y agoThat's why you need not just an Either/Result type, but proper monads so that you can use the same functions with both.
- rixed 2y agoYes, but then you have to handle every possible errors at every call point, and wrap all those you can't handle at the calling site into your own return type... This is well documented. One should be cautious every time it feels like there is an obviously right way to do something that everybody fails to see :)
- lmm 2y ago> Yes, but then you have to handle every possible errors at every call point, and wrap all those you can't handle at the calling site into your own return type... This is well documented. What? No you don't. You can just propagate up the errors you can't handle with the types they come with.
- rixed 2y agoWhat language are you using that will automatically infer this at compilation time?(*) If I understand properly what you are suggesting, to make it work with my goto language (OCaml), I believe that I would have to make every function return a polymorphic variant for the error (aka technicaly equivalent to mandatory try blocks and boxed return types). The only time I had to deal with a library doing that it was not pleasant but it was too long ago for me to remember the details, probably related to complex compilation error messages. (*) looking at your github, I guess Scala?
- The_Colonel 2y ago> They just needed some syntactic sugar to help redirect certain developers into less self-destructive ways of procrastinating on proper error handling. Syntactic sugar it needs is an easy way (like ! prefix) to turn it to a runtime exception. Procrastinating on exceptions is usually the correct thing to do in your typical business application - crash the current business transaction, log the error, return error response. Not much else to do. Instead the applications are now littered with layers of try-catch-rethrow (optionally with redundant logging and wrapping into other useless exceptions) which add no benefit.
- jeroenhd 2y agoThe try/catch/rethrow model can easily be substituted by just adding a `throws` to the method. If you truly don't care, just make your method `throws Exception` or even `throws Throwable` and let the automatic bubbling take care of making you handle exceptions at top level.
- The_Colonel 2y agoThat (or rather checked exceptions in general) doesn't play well with lambdas / streams.
- pinoy420 2y agoHow many errors are actually recoverable. I bet most thrown exceptions could be replaced with a printf(“it went wrong here”) for all their utility.
- The_Colonel 2y agoWell, usually you want to handle it at some level - e.g. a common REST exception handler returning a standard 500 response with some details about what went wrong. Or retry the process (sometimes the errors may be intermittent)
- bluGill 2y agoI disagree. The real value of exceptions is you can skip 6 levels of functions that have lines like status = DoThing(); if(status != allIsWell) {return status;} C++ embedded for a long time has said don't use exceptions they are slow. However recent thinking has changed - turns out in trivial code exceptions are slow but in more real world code exceptions are faster than all those layers if checks - and better yet you won't give up on writing all the if checks. Thus embedded projects are starting turn exceptions on (often optimized exceptions with static pre allocated buffers) The final "print something when wrong" is of little value, but the unwinding is very valuable.
- Cthulhu_ 2y agoI mean on paper they're a good idea and well implemented etc. However, the main two flaws with exceptions are that one, most exceptions are not exceptional situations, and two, exceptions are too expensive for dealing with issues that aren't exceptional like that.
- taeric 2y agoThe problem with checked exceptions is they are best explained and utilized in a single threaded execution model where the exceptions can be bubbled up to the operator. This is not, of course, the only way that checked exceptions can be utilized. But, all too often, that is by far the easiest way to reason on them. They represent a decision that needs to be bubbled up to the operator of the overall system. Worse, the easy way to explain how the system should resume, is to go back to where the exception happened and to restart with some change in place. Disk was full, continue, but write to a new location. That is, having the entire stack unrolled means you wind up wanting the entire process reentrant. But that is an actively hostile way to work for most workflows. Imagine if, on finding a road was closed, a cab driver took you back to pick up location to ask what you want to do about it. If it is not something that you want to unwind the stack, or bubble up to the users, then you go through effort to wrap it so that it is another value that is being processed.
- kflgkans 2y agoThe most common pattern in languages with explicit error handling, is to simply return the error (possibly with some context added) in every function up to the point where the process was started (e.g. an HTTP endpoint handler, or the CLI's main function) to deal with it. I'm not saying exceptions are good, but I am saying that they do represent the most common error handling pattern.
- taeric 2y agoRight, this is largely the same idea. For things that have to be bubbled up, you wind up in the simplistic "single thread of execution by an operator" pattern. And, in that scenario, exceptions work exactly the same as just returning it all the way up. It is literally just making it easier to unwind the stack. My assertion is that actual error handling in workflows doesn't work in that manner. Automated workflows have to either be able to work with the value where it was broken, or generally just mark the entire workflow as busted. In that scenario, you don't bubble up the exception, you instead bubble up an error code stating why it failed so that that can be recorded for later consideration. Along the way of bubbling up, you may take alternative actions.
- mightyham 2y agoI'm not a fan of checked exceptions because they force you to use a verbose control structure (try-catch) that distracts from the actual logic being expressed. Typically, checked exceptions are also not exceptional, so I prefer working with monadic exceptions, like Rust's Option/Result, because they encourage error recovery code to use normal control flow, keeping it consistent with the rest of the application. I also find that generally exceptions can't be meaningfully caught until much higher in the call-stack. In which case, a lot of intermediary methods need to be annotated with a checked exception even though it's not something that matters to that particular method. For this reason, I've really come around on the Erlang way of doing things: throwing runtime exceptions in truly exceptional situations and designing top level code so that processes can simply fail then restart if necessary.
- ben7799 2y agoI've pretty much been stuck on java since 1995, I started right around 1.0. There have been some stints of Python or we have some glue code in C/C++ or whatever but we're talking 95% java. Some of these things are mildly annoying when you think about them in theoretical terms. But all the professional teams I've been on a (a lot) have successfully dealt with exceptions without issue. The teams settled on an exception and error handling process early in the design phase and it rarely has caused a major issue. Yet it seems out on the internet in any place where programming languages are discussed it is an insurmountable problem that has caused all projects in Java to fail and the language died an early and unpopular death. It seems if it caused anyone huge problems it was not Java's issue but that team's issue. There are/were other much bigger issues over the years. Memory leaks have been issues. Spring was hard for many people to deal with back around 2005 or so. The first iterations were quite bad with XML. XML in general caused a lot of issues. J2EE caused a lot of issues just because it was so badly designed early on. (Some of this was because it was birthed out of CORBA, which was itself pretty horrible.) Plenty of issues were caused by using Collections with mixed objects in them early on before Generics were introduced. Visual J++ caused havoc. Different models of web application caused a lot of havoc before we got to Javascript UIs in the browser driven by Web API back ends. JPA was a big mistake IMO. But exceptions were never really a huge problem anywhere. So many have blamed a problem on Java when it was actually a problem with a library, component, or framework written in Java that became way more popular than it should have been. And along the way there were a lot of "developer influencer celebrities" who were listened too far far more than they should have been. Many of these guys (I can't remember one ever being a woman) sold everyone on ultra complex designs and ways of doing things and the community almost always bought in to a ridiculous degree.
- d-man 2y agoI think your exception model needs to match your problem domain and your solution. I work on an Inversion of Control system integration framework on top of a herd of business logic passing messages between systems. If I were to do all over again, then I’d have the business logic: * return success or failure (invalid input) * throw exception with expectation that it might work in the near future (timeout), with advice on how long to wait to retry, and how many retries before escalating * throw exception with expectation that a person needs to check things out (authentication failure) Unless the business logic catches it, unchecked exceptions are a failure. Discussion about what is what kind of exception is hard, but the business owners usually have strong opinions taking me off the hook.