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.
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.
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.
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.
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.
Making a sane hierarchy of exceptions is the smaller of the problems of actually handling them everywhere.
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.
There is not much difference between this and using error types, except the syntax sugar.
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.
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).
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.
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.
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...