-
Notifications
You must be signed in to change notification settings - Fork 18k
Proposal: Go2: Error checking and handling inspired on Switch/Case statements #35086
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Comments
What happens if err is modified by a goroutine to not be nil? What if err is modified while not on the left hand side? What if err is shadowed? |
Nothing. Case statements are trigger only when the handled variable appears in the left-hand side of an assignment operation in the handle scope. |
As much as this simplifies the error handling flow, it also hinders readability. One of the biggest advantages of handling errors after each function is that you can scan a file and understand exactly which functions might return errors and how those errors are handled. |
How does this handle the "locality" of the error ?
How do I know if I got the error in the first statement or the second ? I guess in most cases one would be calling different methods that return different errors (that may or may not be easy to reconnect to the statement that generated the error) but at least in this particular example that wouldn't work. |
What if err is on the left hand side inside of an inline defer function? |
The syntax is really odd, with statements before the case. Is not really comparable to the switch/case statements. It looks more like try/catch where you didn't want to use the try/catch keywords :) |
there's nothing wrong with go error handling, don't ruin things for the rest of us |
I feel like people really don't like (or grasp) the concept that errors are just values. Would you do this pattern for handling bools, ints, strings, etc? I certainly wouldn't create some block within a function that handled those values. Why are we trying to treat errors as something special? |
Because other languages like Java tought us to do so. However, this is neither Go-ish nor idiomatic. Errors are a very common thing to happen... |
"This proposal tries to address an aspect of Go programs which "have too much code checking errors and not enough code handling them" (Error Handling — Problem Overview)." I didn't create that statement. It is perfertly ok to disagree of the statement and the proposal. But please, don't judge me. |
You don't know. This proposal doesn't apply for that user case. |
An inline defer function runs outside of the handle scope, so the case statements are not trigged. |
There being "nothing wrong with go error handling" is completely subjective. Myself and some other gophers believe Go's current error handling it is too verbose and distracting and that Go would benefit from a change here. This proposal appears to add an additional way to handle errors but doesn't appear to force you to change the way you currently code. It might not be an ideal solution but it's interesting enough to read the proposal and see the discussion. It's definitely not rising to the level of "ruining" the language even for those who oppose changing Go. |
This proposal is horrible. It doesn't look or feel like Go and it hides the origin of the error. Errors are just values, treat them as such. |
I do not agree with your statement. Errors are something special. Errors are, in a sense, a representation of a non-complete computation. |
I see value on your proposal. Try and make it using go code, creating a module with handle as a variadic function, receiving any argument, and then checking on all errors founded. |
How can I know if func1 or func2 failed even in both? func doSomething() error {
handle i int {
i = func1()
i = func2()
case i == -1:
return fmt.Errorf("Error: out-of-bound")
case i == -2:
return fmt.Errorf("Error: not a number")
}
} |
You can't. This proposal tries to address an user case which has an homogeneous error handling. |
Is it really worth it to include as a language feature then, just for that use case? Even then, it makes a program using this proposal harder to reason about in error states. |
@conilas et al please read this: https://blog.golang.org/errors-are-values |
In my opinion the original Go2 proposal for error handling didn't go through because, like this, only solved one of the use cases that people wanted (reduce the boiler plate). Turns out developers wanted more: reduce the boilerplate, wrap errors, etc. Before coming up with another proposal for error handling we should really first decide what is the minimal set of expectations that a proposal for new error handling should satisfy. |
Why/how do you think go's err handling is too verbose? |
Right, but I think we should always reference go's design philosophy before potentially adding features; maybe we can poll? |
It forces the same wordy construct over and over. It's my opinion, of course, but dropping If I'm not going to be able to do anything about an error or exception but essentially pass it up the call hierarchy then I'd rather have something that's a bit off happy path and would leave the non-exceptional logic intact. If you believe demarking each place where a potential error can occur is important to reading the code then you're probably more inline with the Go's original intent. I think the original intent was an interesting idea (even ideology) but hasn't turned out as well in practice. I'm not opposed to changing the language at this level to add more practical options. Note: there are already a couple patterns that can help with this but they're certainly not as elegant as what could be achieved by integrating a solution within Go. |
What do you think about the current ubiquitous try, catch, finally err handling method in general? |
If you think that Go's design philosophy was correct and works well for your uses then I agree. If you believe, like I do, that Go's design philosophy was good but not perfect and could now use a bit of correction then adding or changing features seems appropriate. Go's philosophy for errors was that they're values to be be coded against like any other conditional return value. This turns out to be correct some of the time but not all the time. Or maybe all of the time if you're really wedded to wanting them to be. I think Go would benefit from considering them from additional perspectives too. |
yeah |
I think try/catch is excellent for certain types of errors. Database errors in particular but errors where you need to ensure resources are cleaned up work well with try/catch. Try/catch is nice because it sits a bit above the happy path. One try can hold a half dozen error points so you often don't need to think about the error conditions at all in that section of your code. If you hit the catch you get the error so you can inspect if if that's of value. You also can pick up the stack trace too and use it or pass it along as appropriate. There are a couple ways to simulate try/catch in Go already, but they involve defer and defer can suffer from scoping and timing edge cases. I don't think they're as clear. That said, try/catch is horrible for so many classes of errors I can understand why Go's designers wanted to try another way. For me, I'm not opposed to having multiple ways of handling errors in Go. "x, err :=" is nice for many (most) situations. But I would like it if a less verbose version were available in Go. Errors/exceptions are diverse enough I don't think a one size fits all solution is going to be found in the Algol language tree. |
FWIW, quite some time ago I came up with an error handling syntax that (1) keeps all existing code working and (2) handles about 40-50% of the boilerplate error code in my code. Two (or three) language changes. It would reduce this
to this
Here's the gist:
With one more change you could allow passing a string into the error handler at the call site:
|
that sounds good! |
The proposed |
If I understand correctly, it would execute the statements until one of the conditions is met. for_all_statement { // do every statement inside
x, err := a()
y, err := b()
} // this is basically a block
var err error
for_all_statment err == nil { // check condition after every statment
x, err := a()
y, err := b()
}
for_all_statment err error; err == nil {
x, err := a()
y, err := b()
}
for_all_statment i := 0; i < 100 { // can be used for other things too
i += countA()
i += countB()
i += countC()
}
for_all_statment err error; err == nil {
x, err := a()
y, err := b()
} else { // what to do if block got broken
fmt.Println(err)
}
// maybe this would be nice too
var x int32
var err error
for_all_statment {
x, err = a()
x, err = b()
} do { // placeholder name
if err == nil {
if x == 0 {
fmt.Println("success but still breaking")
break // optionally with label
}
} else {
fmt.Println("breaking due to error")
break
}
} |
According to emoji voting, there is no strong support for this change. There are several comments above pointing out difficulties. For these reasons, this is a likely decline. Leaving open for four weeks for final comments. |
No final comments. Closing. |
Proposal: Go2: Error checking and handling inspired on Switch/Case statements
Summary
This proposal tries to address an aspect of Go programs which "have too much code checking errors and not enough code handling them" (Error Handling — Problem Overview). To solve this, the proposal is inspired by Switch/Case statements in Go.
Introduction
This proposal creates the new statement, handle, that defines a scope where one variable is checked and handled as soon there is assignment operation with that variable. That variable is called "handled variable". For example, the following code is how Go handles errors:
In this proposal, the function could be the following:
The handle statement defines a handled variable (err in the above example) and a scope where this variable is handled. In the handle scope, each time the handled variable is in the left-hand side of an assignment operation, immediately after that assignment operation, it will be executed the checks defined in the case statements.
The case statements are evaluated top-to-bottom; the first one that is true, triggers the execution of the statements of the associated case; the other cases are skipped. If no case matches no one is executed. There is no "default" case.
Defining error handled-variable in the handle statement
The handled variable can be defined in the handle statement, so the code can be even shorter.
Example: Error handling with defer statement
In Golang:
In this proposal:
Example: Error handling without return
In Golang:
In this proposal:
Handling non-error variable
It is possible to handle non-error variables, like:
Nested handle scopes
There are some hard decisions to make the nested handle scopes works properly. So, this proposal suggests to forbid nested handle scopes.
The text was updated successfully, but these errors were encountered: