-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improved exception hierarchy/types #11639
Comments
Duplicate of #8990? |
@asterite Not really a duplicate, but maybe a restart? Thanks for digging that up though. I was sure there was some more previous discussions about this, but couldn't find that one. |
I think there's deff a path forward without For example, maybe something like this: Exception
├── DivisionByZeroError
├── Base64::Error
├── Channel::ClosedError
├── Crystal::ELF::Error
├── Enumerable::EmptyError
├── InvalidByteSequenceError
├── Path::Error
├── Time::Error (maybe)
│ ├── Time::FloatingTimeConversionError
│ ├── Time::Format::Error
│ ├── Time::Location::InvalidLocationNameError
│ ├── Time::Location::InvalidTZDataError
│ └── Time::Location::InvalidTimezoneOffsetError
├── IO::Error
│ ├── File::Error
│ │ ├── File::AccessDeniedError
│ │ ├── File::AlreadyExistsError
│ │ ├── File::NotFoundError
│ │ └── File::BadPatternError (This currently inherits `Exception`?)
│ ├── IO::EOFError
│ └── IO::TimeoutError
├── RuntimeError
│ ├── OSError (new, deprecate `RuntimeError#os_error` in favor of this type)
│ ├── KeyError
│ ├── IndexError
│ └── OverflowError
└── LogicError (new)
├── ArgumentError
├── TypeCastError
├── NotImplementedError
├── NilAssertionError
└── DomainError (maybe) Wasn't really sure what to do with those random ones towards the top, so just kept them as direct children, which maybe means they should really be
EDIT:
|
Thanks for starting this. I think there is a lot room for improvements exceptions in Crystal. So this is a very extensive topic. I'm listing a few talking points that I feel should be addressed at some point. Some may not be very clear, those mainly serve as a memo for myself 🙈
|
Has there been any discussion around allowing exceptions to be rescued based on an included module? This would make exceptions a bit more flexible by allowing users to more easily rescue groups of related exceptions, while still having them be reusable via a parent level E.g. module MyApp::Exception; end
class MyApp::Exception::SomeError < ArgumentError
include MyApp::Exception
end
begin
# ...
rescue ex : MyApp::Exception # Rescue all exceptions within `MyApp`
end
begin
# ...
rescue ex : ArgumentError # Rescue all forms of `ArgumentError`
end A workaround for this is to use an |
I'm wondering about the purpose of such exception grouping. What would be the purpose for another level of hierarchy on top of class inheritance? Your example shows a namespace/app specific exception grouping. Why would I want to rescue exceptions from a specific namespace if they're semantically unrelated? If they'd be semantically related, they would be in a common hierarchy tree. |
My thinking is they're logically related since they're defined in the same namespace of a singular project. One use case I can think of is within related libraries, say A and B. Library A wraps library B in that it provides extra functionality on top of a more basic implementation. Library B has a portion that has user defined code within it. Library A may want to rescue all exceptions from library B such that it can better handle/report the error (e.g. incorrect usages of A/B features) while allowing user code exceptions to bubble up. Granted this is pretty abstract, but is there an actual reason for not supporting it? It actually seems Ruby allows this fwiw. |
For reference, the current hierarchy is like this: (generated from
|
Somewhat related: I think it would also be nice if stdlib could refrain from raising String ( I recently bumped into this when writing a pooled HTTP client My handler-code has to rely on the text message, which doesn't feel right: rescue ex
if ex.message == "This HTTP::Client cannot be reconnected"
# ...
end I think it should rather be: rescue ex : HTTP::Client::CanNotBeReconnectedError
# ...
end Currently there appear to be ~200 places where String is raised: Imho that's fine in the compiler and inside macros but |
I don't think raising a more specific exception type would break anything, but what about raising a less specific, or unrelated type? Let's say |
With #14552 we have some extra options when it comes to setting up the hierarchy as we would no longer be limited to inheritance. E.g. we have the ability to treat some of these as interface mixins vs requiring them to be dedicated types. This would allow having "exception" types that can be recused but not directly raised themselves. Not exactly sure how this could/would fit into things, but is at least another possibility in our toolbox. In the shorter term, thoughts on starting things off by:
|
After dwelling on this for a while I'm now unsure if |
Discussion
The types and hierarchy of exception classes.
Currently Crystal exception types are essentially all direct children of
Exception
. This makes it a bit challenging to rescue "groups" of related exceptions in a more general fashion. Similarly there could be room to differentiate between something that could be gracefully handled versus something that could not.In the end having these higher level more generic exception types gives shard creators more flexibility in describing how their custom exceptions should be handled. I.e. automatically handled with a more general type, or something on its own.
For example being able to rescue/check for a
LogicError
type that could use a different form of logging/messaging since it should directly result in a change in your code. This may also help with an implementation of Ruby'sthrow
? 🤷Other langs handle the latter point via having a
Throwable
(Raisable
in our case), thenException
andError
subtypes. From here exception types are grouped into related categories. I don't have anything concrete as of now, but using other lang's hierarchy as a base and figuring out what makes the most sense to include would be a good start.The text was updated successfully, but these errors were encountered: