20 ms·
Why checked exceptions failed
- hota_mazi 3y agoChecked 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 3y agoIt'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.
- jdm2212 3y agoChecked 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.
- ratherbefuddled 3y agoThough 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 3y agoI 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.
- jayd16 3y agoThe 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.
- amalcon 3y ago"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.
- mr_00ff00 3y agoPersonally if I were to argue why rust error handling is better than Java it wouldn’t be the too much code part. I feel the same way about Go vs Java or Zig vs Java, I think errors as values makes more sense. It’s the laws of the universe breaking and control flow changing on errors that I think most people hate.
- jdm2212 3y agoYeah, that'd be nice.
- jayd16 3y agoIs 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.
- jdm2212 3y agoYou can return union types in Java if you want that, although it'd be nice to have better first class support for them. The issue though is that forcing people to handle irrecoverable exceptions is just kind of dumb. If I do file IO, I know it'll barf sometimes. Sometimes I care, but the vast majority of the time I don't. A lot of Java servers have RPC endpoints that basically say "write X to file Y" or "read X from file Y" and if the S3 connection is busted, there's nothing the server can do about that. Someone has to go log spelunking, figure out what's wrong (network is misconfigured, AWS is having an outage, whatever) and fix it. So it is bad API design on Java's part to make every single one of those RPC endpoints wrap every single thing that does file IO in a try/catch.
- Gibbon1 3y agoI 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.
- bluepizza 3y agoI don't think they are meant to be recoverable from, but instead they are a way to provide a controlled shut down of an irrecoverable failure, or to limit the blast radius of a localized failure. In that sense, they are quite useful. Saving the file generated an exception - instead of suddenly closing the application, display an error. Or maybe a database is not accessible anymore. Instead of suddenly ending the service, trigger a controlled shutdown (log, send alerts, etc). I don't think anyone would expect exceptions to propagate through RPC calls. A call fails, probably containing a description of the fail. Why should it propagate? There are ways to handle errors and return errored RPCs without exceptions, such as internal error codes. But error codes are just as irrecoverable as exceptions.
- jdm2212 3y agoIn a desktop application that is not allowed to totally crash, or at least has to crash kind of gracefully, checked exceptions are useful. But in the world that most Java devs live in, which is various flavors of RPC server, failing requests is fine. If lots of requests fail, your monitoring infra should page someone, and that someone will go log spelunking and figure out what's broken. Very occasionally it turns out that the thing causing the RPC failures is a recoverable exception, and then you should wrap the problematic stuff in a try/catch block. (Often you'll wind up having to detect the recoverable error case by conditioning on substrings in the exception message, which the library owners will arbitrarily change in future releases. So make sure to write regression tests so you'll catch this when you upgrade the library. Java is fun!) But 99% of the time the failure is "network is busted" or "config was invalid" or "hard disk failed" and you should not be defensively programming against all those possibilities.
- bluepizza 3y ago> you should not be defensively programming against all those possibilities This is where libraries and frameworks come into play - they defensively program against that for you. And wrap it all up in a simple interface with, well, checked exceptions.
- 3y ago
- cvoss 3y ago> 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 3y agoThe 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.
- aib 3y agoI think the reason is simpler: Unchecked exceptions. Investing in (checked) exception safety is not as valuable when anything can throw an NPE.
- jacquesm 3y agoErlang 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.
- smrtinsert 3y agoThey didn't fail. I find them useful and great in code reviews where I can see juniors trying to get away with bad practices because they don't feel like making the possible issues known to business owners.
- adamwk 3y agoI think Swift’s error model is my favorite (though Go’s new error stuff seems similar). Unrecoverable errors are unrecoverable (they’re aborts) and other errors are marked in the function’s signature and must be handled explicitly. This means the error effect is explicit along the call stack until somebody handles it
- necovek 3y agoI 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 3y agoThis is indeed the supported way to do it.
- necovek 3y agoThanks 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 3y agoIt 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 3y agoMaybe 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...
- DamonHD 3y ago"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.
- o1y32 3y agoThis is a very old topic. Insights from Anders Hejlsberg: https://www.artima.com/articles/the-trouble-with-checked-exceptions https://www.artima.com/articles/the-trouble-with-checked-exc...
- hodgesrm 3y agoGreat 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.
- redeeman 3y ago> 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 3y agoFor 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
- rcme 3y agoThe 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.
- computerdork 3y agoTo 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.
- inglor 3y agoExactly, 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.
- javajosh 3y agoChecked 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 3y agoJust 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 3y agoExceptions “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.
- throw3823423 3y agoBut 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.
- invalidname 3y agoMy opinion is the exact opposite see https://debugagent.com/everything-bad-in-java-is-good-for-you 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.
- throwawaymaths 3y agoNot 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.
- scarface_74 3y ago> 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 3y agoWe 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 3y agoWhat “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.
- breadwinner 3y agoI 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.dlang.org https://forum.dlang.org/thread/hxhjcchsulqejwxywfbn@forum.dl...
- osigurdson 3y agoExceptions are great when they don’t need to be caught. Otherwise they are a pain.
- _a_a_a_ 3y agoEach '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.
- funnymony 3y ago“I wrote some very good C# code“ We all do.. its always that other guy who is at fault :)
- scarface_74 3y agoCatching 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.
- he0001 3y agoChecked 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 3y agoThey 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 3y agoNot 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.
- cubefox 3y agoWhile 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.)
- Blikkentrekker 3y agoThe problem with union types for exceptions is that it becomes impossible to return an error as the correct case, or at least there is no way to discriminate any more. Functions that deal with errors also need to be able to have exceptional situations, and in many cases there are completely generic functions as well that can error out for generic reasons. A basic example is the elementary case of having a list of errors for some reason and indexing out of bounds producing an error, what if one have a list of values, which are all derived from itself accessing lists, each of which contains “Ok:Val+Err:OutOfBounds”, accessing that list itself now becomes indeterminate. The other issue is that the language needs to have support for union types rather than merely sum types. And there's a good reason almost no statically typed language has support for union types since it removes provability and it's not something that can in easily be statically decided, requiring dynamic type tags, but then again, that's what sum types effectively are as well. Typed Racket is statically typed and has union types, but it's an extra layer on top of a dynamically typed language, so the type tags where there already, but it's not easy to erase type data at run time not to implement. In your example of “String|Error”, it essentially requires that every String is now tagged at runtime with tag information about it in practice.
- TrianguloY 3y agoAs 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! )
- grumbel 3y agoLittle 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.
- mostlylurks 3y ago> 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 3y agoYep, 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 };
- senkora 3y ago> 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 3y ago
- ndufjfhfufjdufj 3y ago[flagged]
- EricRiese 3y agoJava 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 3y agoThe 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.
- tetha 3y agoYeah. 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 3y ago> 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.
- mkleczek 3y agoChecked 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 3y agoNo, 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 3y ago> 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 3y agoMaybe we do and I can't read. :-)