-
-
Notifications
You must be signed in to change notification settings - Fork 48
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
Stateful exceptions #76
Open
Praetonus
wants to merge
4
commits into
ponylang:main
Choose a base branch
from
Praetonus:stateful-exceptions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
- Feature Name: stateful-exceptions | ||
- Start Date: 2017-01-27 | ||
- RFC PR: | ||
- Pony Issue: | ||
|
||
# Summary | ||
|
||
Allow exceptions to carry a value from the error site to the handling site, while keeping the exception handling static. The value or its type aren't involved in destination checking. Exceptions will still land at the first handler encountered, with negligible additional runtime cost. | ||
|
||
# Motivation | ||
|
||
We currently have two very distinct idioms to handle errors in the language and standard library. | ||
|
||
1. Exceptions. They are used most of the time but since they are valueless, they can't be used to propagate the reason of an error to a caller function. | ||
2. Union types of the normal result and the error reason(s). This is used when the reason of an error is needed by a caller function (e.g. the constructors of `File` in the standard library). | ||
|
||
Number 2 has several drawbacks. In particular | ||
|
||
- The type of the result must be asserted via pattern matching, which introduces a runtime cost even in the non-erroring cases | ||
- The error condition must be propagated manually through every calling function, unlike a "fire and forget" exception | ||
|
||
In addition, having two different ways of doing almost the same thing isn't good for the overall consistency of the language and libraries. | ||
|
||
Having this feature would also make the exception system a lot more versatile. By default it will still permit fast and static exception handling, while allowing programmers to manually implement dynamic handling systems akin to "traditional" languages like Java. | ||
|
||
# Detailed design | ||
|
||
## The raising part | ||
|
||
`error` will now take an optional expression as its right-hand side. This expression will be the value passed to the exception handler (i.e. the `else` of a `try` expression). The error value must be a subtype of `Any val`. The reasons for this are: | ||
|
||
- The value must have a type on the handling side. `Any` is a natural choice here (because exception specifications are still static) and `val` is a compromise between the broad and not-so-useful `tag` and the very restrictive `iso`. This will allow erroring with primitives, `String`s, etc. | ||
- This can't violate any capability boundary and avoids additional heavy checks. | ||
|
||
An `error` with no value implicitly defaults to `error None`. | ||
|
||
## The handling part | ||
|
||
A new special value, `current_error`, will be accessible in the `else` branch of `try` expressions. This `current_error` will be an alias of the `error`ed value. Its type is `Any val`, the real type can be established through pattern matching to use the original value. `current_error` always references the value handled by the closest `else` block (i.e. the most nested one). | ||
|
||
This new mechanism doesn't change anything to the actual exception handling. Exceptions still stop at the first handler encountered. | ||
|
||
## Implementation and performance concerns | ||
|
||
This change can be implemented with very little overhead. The cost roughly is an additional argument to a runtime function call, an additional write to memory (when raising) and an additional read from memory (when beginning handling). These operations are negligible compared to the overall cost of raising an exception. | ||
|
||
A proof-of-concept implementation can be found [here](https://github.com/Praetonus/ponyc/tree/stateful-exceptions) (untested on Windows). | ||
|
||
# How We Teach This | ||
|
||
We'll update the tutorial section on exceptions to explain how to raise an error with a value and how to process that value in the handler. | ||
|
||
We could also do a Pony Pattern explaining how to emulate a dynamic exception system on the user side through pattern matching and successive re-raising of errors. | ||
|
||
# How We Test This | ||
|
||
We'll add some type checking tests to ensure type validity on both the raising and the handling side. While we're currently lacking that functionnality in the test frameworks, having tests ensuring that the value is propagated correctly would be good. These tests would have to wait until we can run Pony code in JIT through the compiler and tests. | ||
|
||
# Drawbacks | ||
|
||
`current_error` would become a reserved identifier. Otherwise, backwards compatibility is fully maintained. | ||
|
||
# Alternatives | ||
|
||
- Implement a full-blown dynamic exception system. This would be a really important performance hit on most programs, while not having many advantages over the proposed system. | ||
- Keep things as is. This would leave the concerns raised in Motivation unresolved. | ||
|
||
# Unresolved questions | ||
|
||
None. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For completeness, the third idiom would be having a notifier object that will be invoked for any errors encountered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add that.