-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: Go 2: on err
or catch
statement for error handling
#32611
Comments
I was just thinking how inlining a handler would look like. on err, {
do()
things()
return err
} The comma doesn't feel right in that case. |
I would opt for a
|
The comma between "on err, return" bugs me, as comma is most commonly used between two variables.
I think the above proposal offers a wide variety of solutions as you stated:
And even some more, but I'm not sure there's a bandwidth to address it, I tried to advocate a rating mechanism for try() or any other proposal (BTW, can be a good concept not only for the Go 2 error handling improvements but a general cost-effective per major development effort criteria), but I dont see the concept is well understood, If there's no bandwidth the options are: |
@networkimprov I agree a conditional return without assignment (ie, not
What if it was 1 line
There is already a history of using
|
This proposal is missing the "precise statement of the proposed change" part.
where X must have type error, and which expands to
Is that a correct and complete statement of what you are proposing? |
@rsc, thanks for pointing that out. I've added a Specifics section, which says
The expression is constrained to ensure that its use (if any) within the statement is correct. For example, expression cannot be a function call or channel receive. It could be limited to an lvalue, literal, or constant, IOW, it's not specific to type EDIT: I've also added a Possible Extensions section. |
As you hinted at yourself, the proposed syntax doesn't gain much over just putting the equivalent
I don't think it's worth making a breaking change to the language for something you can almost get with a |
I don't get the point of "but if we have on line if-statements". We do now have them, and AFAIK the go team doesn't want them. We don't have them for a reason. If we add them that would pretty much nullify this proposal, but that doesn't mean that this proposal is any worse because of it. |
I support this proposal. It is important that any change to error handling allows each error in a function to be annotated explicitly and independently from other errors. Returning the naked error, or using the same annotation for every error return in a function, may be useful in some exceptional circumstances, but are not good general practices. I also support using a keyword instead of a builtin. If we add a new way to exit a function, it is important that it isn't nestable within other expressions. If we have to wait until further improvements to the language allow this to be accomplished without breaking the Go 1 compatibility promise, then we should do that, rather than make the change now. |
@peterbourgon Can you explain why you think Also, there is a mantra that every error needs to be decorated somehow. I agree that this is important at the library boundary level. But there are plenty of package interior functions where decorating an error is not needed; in fact it only leads to over-decorated errors. Can you expand on the statement of bad practice that you are referring to? |
To be clear, I'm in the "leave if err != nil alone" camp, like many other veterans of the language. But it seems like the core team has decided this is a "problem" that's worth solving, and I have sympathy for the perspective that it might encourage more not-yet-Gophers to become Gophers, so I'm speaking from that assumption. edit: From that assumption, I think it's important that any proposal not affect the semantics (for lack of a better word) of error handling as it exists today (by which I mean that errors are generally totally handled adjacent to the error-causing expression) and instead affect only the verbosity of error handling. And the only commonality between the error handling blocks I write which could be reduced are if err != nil { // <-- this line
... //
return ... // <-- this keyword
} // <-- and this line For that reason, I feel like
I think this is one of many points that demonstrate a disconnect between the core Go team and Go as it is used in practice, at least as I have always experienced it. Over the years I've taught dozens Go courses, consulted for nearly a hundred companies, small and large, startups and Fortune-500; I suspect I've interacted with thousands of Go programmers, and seen nearly as many discrete Go codebases. They are almost exclusively private, and their developers infrequently respond to surveys. As I see it, this is the primary market for Go the language. (Of course, I may be wrong. Grain of salt and all.) In these contexts there are almost no packages which are imported by more than one binary. In fact I'd estimate that ~50% of Go code I've seen has just been in func checkErr(err error) {
if err != nil {
panic(err)
}
} So, given this reality, where package boundaries are rarely well-defined, where functions instead serve as the primary way to hew borders in programs, the risk is never over-decoration. I've never once given a code review where I requested the author to de-annotate an error because it was too noisy. But I've given plenty of reviews where I've had to convince the author to remove their checkErr helper or equivalent and actually return errors at all rather than just terminating the process from somewhere deep in the callstack. So if a new language feature provides most of the "I don't have to think about this" convenience of checkErr and requires something like a deferred block to do annotation, I'm positive that almost no developer as I've described them will perform any annotation whatsoever, and I fear we'll move the resiliency and coherence of most Go codebases backwards, not forwards. Of course, |
Thanks, @peterbourgon for your detailed comment. We've picked up error checking because the community has repeatedly and strongly complained about the verbosity of error handling. And yes, we also think that it might be nice if there were a less verbose approach where possible. So we're trying to move this forward if we can. From what you're saying, I see your point that (As an aside, I've written plenty of little tools that just panic when I don't care about the error - not great, but good enough when time is of the essence. |
@peterbourgon @griesemer, the last few comments haven't considered that |
I've added error inspection to the Possible Extensions section:
|
Does this proposal reinvent C semantics for if and calls it on? The statement is executed if the expression is non-zero. The two differences are that C allows compound statements (aka blocks) and supports additionally an else branch. |
If we're at a new keyword, why not generalize it as an "alias" to the
on err {
return err
}
on !err {"\n"}
on ok := call(); ok {"\n"}
on ok := call(); !ok {"\n"}
on !(x%d) {"\n"}
on a - b {"\n"} Then after a slight reduction, we may just add this pinch of implicit nonmagic Magic semicolon rules, with the closing parenthesis relaxing rule are more magic than that. if err {
return err
}
if !err {"\n"}
if ok := call(); ok {"\n"}
if ok := call(); !ok {"\n"}
if !(x%d) {"\n"}
if a - b {"\n"} The real change would be to allow single statement blocks to occupy a line. But in fact I now value that space hinting at the possible change of the control flow. @ulikunitz Yes, indeed :) |
I think the boilerplate that counts is what people see. This reduces three lines to one line. It reduces This adds a new kind of constrained expression which does not exist elsewhere in the language. The suggested extensions seem to mostly try to unconstrain the expression a bit by writing To me personally I don't think the savings are enough to justify the language change. |
For me personally with PS. (Edit)
But wasn't this given as a rationale for the (I remember your rebuke to mine (at go-newbie times) proposal of iirc |
@ianlancetaylor there's nothing like What Sure, the concept needs work to support a variety of use cases in a way that blends with the rest of Go. |
I did not mean to imply that it was not OK to devise novel schemes. But any novel scheme is a cost, which must be balanced by some benefit. I was listing the benefits and costs I see of this scheme. |
Perhaps so, but it would still benefit the debate for Go team members to state publicly whatever personal objections they have. I know a few hundred people who'd like to hear them :-)
We could define
|
If the members of the Go team have personal objections to the |
As discussed above in several different comments, this proposal adds language complexity while removing little of the error handling boilerplate. Given that there is little enthusiasm for eliminating error handling boilerplate alone, the cost of this proposal is simply not worth the marginal benefit that it provides. This proposal is basically a different way of writing an -- writing for @golang/proposal-review |
Forgive me if this is the wrong place for this feedback.
I guess this may be getting at what Robert wrote in the closing comment to the try proposal, when he noted that it’s worth clarifying what the problem we’re trying to solve actually is. But I challenge this summary: in my opinion it is precisely and only the boilerplate that should be reduced, the current semantics—error handling as separate statements/blocks directly adjacent to the error-generating code—are important and foundational and should not be changed. edit: Some questions that we should probably come to a rough consensus on as a community, before spending too much time on future proposals, include, in my opinion:
|
@peterbourgon I don't think I agree with what you say but I doubt this is the best place for this discussion. In any case I don't think that discussion affects the decision on this issue. No matter what you consider to be boilerplate, this proposal doesn't remove enough of it to be worth the cost. |
I don’t agree that this proposal doesn’t remove enough boilerplate (edit in response to below: my rationale), but that is of course subjective. I would love to have a more productive conversation about this; if the time and place for that conversation becomes apparent, please keep me in the loop. |
My analysis of the boilerplate for this specific proposal can be seen at #32611 (comment) . |
I think option 3. from networkimprov has been underestimated (to quote): If gofmt were to allow single-statement if-statements for err != nil to appear on a single line, I come from C and I greatly value the enormous effort and experience that has gone into making A 3-line boilerplate handball up the stack of an error from a called function is not the end of the But ... in libraries I wrote in C, I returned an error value from almost every function and handballed it up the Where an error needed to be originated, or a returned error needed to be decorated, I did that on There was no error handling clutter, except where it counted: new error generation, and decoration. Error generation, decoration and handballing was clear and uncluttered. And that was achieved by
|
@urban-wombat You already opened #33113 for that. I suggest that if you want to mention it on other issues that you point to that one. That seems better than scattering discussion over a range of different issues. Thanks. |
@peterbourgon I re-read your earlier rationale, and I still feel that this specific change doesn't make enough of a difference to make it worth changing the language. This is particularly true given the additional non-orthogonal concepts that this change implies: a new restricted form of expressions and a new statement syntax. |
Hi @peterbourgon, some quick comments. This current proposal is fairly broad, including the proposal itself has a fair number of permutations Sometimes, "broader" is good because it means the solution is more general. In other cases, "broader" increases the cost of a change, or increases overlap with existing alternatives. For example, as originally written, as far as I followed, any zero value is supported and it is not specific to error types, which would mean something like this is supported under the original proposal here:
In that case, that is a broader capability than strictly targeting error handling (which in turn requires a judgement call as to whether or not it is good to be broader in that way). I think there is a narrower form of this proposal that is closer to what I think I might have seen you sketch roughly as:
In any event, because this proposal has a fair number of permutations, in some cases it has been hard at least for me to understand which permutation a specific comment is addressing. (Finally, I am speaking for myself as a more-or-less random gopher from the broader community. In other words, just to be explicit, I am not member of the core Go team). |
There is no need to continue this discussion. If you read carefully, it's clear that the Go team was only interested in: IOW, only an expression like Thanks for everyone's interest, |
This should be confirmed by the Go team and not merely assumed. (This is also what I was driving at with my set of scoping questions here.) |
Speaking purely personally, I don't agree with @networkimprov 's comment at #32611 (comment) . We don't have specific requirements of that sort. If we did, we would say them. We do our best to judge each proposal on its own merits. Every language change has costs and benefits. I want to stress, again, that this specific proposal introduced new concepts into the language, and that is a cost. We need a benefit that is worth that cost, and in the opinion of the proposal review team the benefit of this specific proposal was not worth the cost of this specific proposal. To conclude from that that the Go team is only interested in "zero lines of boilerplate" or "no changes to assignment LHS" is to misunderstand what we are saying. We are weighing costs and benefits. We are not saying that only certain kinds of changes are acceptable. We are saying that the benefits of a change must outweigh the costs of a change. |
I don't believe you're misleading us, but I stand by my assessment of the Go team's interests. You write as if "costs" and "benefits" were objective measurements. They are subjective in this case, and therefore influenced by preferences which one may not be fully aware of. |
You're right of course that costs and benefits are somewhat subjective. As has been said, Go is an opinionated language. That said I think that you are simply mistaken in the conclusions that you are drawing. I think it's clear that Go has considerable tolerance for boilerplate. I don't see how it's possible to conclude that the Go team would only accept a proposal that reduces boilerplate down to zero lines. I agree that additional syntax on the left hand side of an assignment statement is a bigger lift, since there is nothing like that in Go today. Currently the left hand side of an assignment must be addressable, be a map index expression, or be the special identifier But to say that any of these ideas are ruled out a priori is to misunderstand the nature of costs and benefits. Even if costs and benefits are subjective, which to some extent they are, it doesn't mean that the costs can not be incurred. It just means that they need a corresponding benefit. Clearly there are some changes that will never be made to Go. For example, we will never break significant amounts of existing Go code; that is a cost that is so high that it's impossible to imagine a corresponding benefit. But I don't see why the kinds of changes we are talking about here rise to that level. I could very well be wrong, but it seems to me that in these discussions you persistently underestimate the costs of a language change. That underestimation, if real, may lead you to conclude that the Go team must not see the benefits of the change. I encourage you to seriously consider that perhaps we do see at least some of the benefits, but that we judge the costs differently than you do. Above you tried to separate cost to the spec, cost to the users, and cost to the tooling. That is very different from the way that I view the costs of language changes. The only cost that matters is the cost of understanding the language. Every special case in the language (and the language has far too many special cases already) is a cost. The goal is a language that is simple and orthogonal. Every concept in the language should ideally interact with every other concept clearly and simply. When certain kinds of expressions that can only be used in one place, when we add certain kinds of statement syntax that can only be used with one statement, those are big costs. As you know, the discussion of the |
A belief that "the only cost that matters is the cost of understanding the language" may guide you into future community resistance, because we are all acutely aware of what I called the "cost to the users/ecoysystem" #32611 (comment). I based my assessment above on statements by Robert and Russ in other discussions. And "zero new statements" is an equivalent criterion to "zero boilerplate lines". Re cost of a language change, the Go team was first to propose such a thing: check/handle. That's all I'll say on the matter, and you'll be relieved to hear that I have no strong opinions on generics :-) |
I continue to disagree that "cost to the users/ecosystem" is distinct from what I am considering. I want to make clear that I did not say or imply anything like "zero new statements." |
Thanks ianlancetaylor, point taken. See my further comment elaborating on proposal #33113 and especially the Go Playground code samples:- |
The case for the proposed gofmt change seems even stronger when named
outputs are used. Here's a variant of urban-wombat's second Go Playground
example.
https://play.golang.org/p/_X_RTEKD1py
…On Thu, Jul 18, 2019 at 11:06 PM urban-wombat ***@***.***> wrote:
Thanks ianlancetaylor <https://github.com/ianlancetaylor>, point taken.
See my further comment elaborating on proposal #33113
<#33113> and especially the Go
Playground code samples:-
1. Current gofmt formatting <https://play.golang.org/p/3vUlTc1TGwj>
2. Proposed gofmt formatting <https://play.golang.org/p/zsWYS1FB11M>
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#32611?email_source=notifications&email_token=ABTDSJNBIWHMOPL737K7OHLQAEVUPA5CNFSM4HYAP3Z2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2KNYIY#issuecomment-513072163>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABTDSJNJJUASTCSXFYQBTMTQAEVUPANCNFSM4HYAP3ZQ>
.
|
May I impose on the last two commenters to re-post their comments on #33113 and delete the copies above? Thanks. |
… On Fri, Jul 19, 2019 at 8:34 AM Liam ***@***.***> wrote:
May I impose on the last two commenters to re-post their comments on
#33113 <#33113> and delete the copies
above? Thanks.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#32611?email_source=notifications&email_token=ABTDSJMDPU7ULTFI3T6HO53QAGYHFA5CNFSM4HYAP3Z2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LPPWA#issuecomment-513210328>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABTDSJOXWEINKWNNSK3U77TQAGYHFANCNFSM4HYAP3ZQ>
.
|
Let's not do it then
On Fri, 19 Jul 2019, 13:48 Michael-F-Ellis, <notifications@github.com>
wrote:
… @liam Doesn't seem worth the effort since #33113
<#33113> was summarily closed.
On Fri, Jul 19, 2019 at 8:34 AM Liam ***@***.***> wrote:
> May I impose on the last two commenters to re-post their comments on
> #33113 <#33113> and delete the copies
> above? Thanks.
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <
#32611?email_source=notifications&email_token=ABTDSJMDPU7ULTFI3T6HO53QAGYHFA5CNFSM4HYAP3Z2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LPPWA#issuecomment-513210328
>,
> or mute the thread
> <
https://github.com/notifications/unsubscribe-auth/ABTDSJOXWEINKWNNSK3U77TQAGYHFANCNFSM4HYAP3ZQ
>
> .
>
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#32611?email_source=notifications&email_token=AAAGRFC7T7WTIC6CI3YHS5TQAGZ2DA5CNFSM4HYAP3Z2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2LQPZY#issuecomment-513214439>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAGRFEANUH6V3QBKX2GD3DQAGZ2DANCNFSM4HYAP3ZQ>
.
|
The
try()
proposal #32437 has met with significant complaint, especially abouta) lack of support for decorating/wrapping returned errors
b) lack of support for widespread alternatives to returning error, e.g. log.Fatal(), retry, etc.
c) obscured function exit
d) difficulty of inserting debugging logic
If a separate line is usually required for decoration/wrap, let's just "allocate" that line.
(This may overlap too much with
if ... { <stmt> }
. An error-specific variant is given in Critiques no. 1.)This plugs easily into existing code, as it leaves the err variable intact for any subsequent use.
This supports any single-statement action, and can be extended with named handlers:
Keyword
on
borrows from Javascript. It's preferrable not to overloadif
.A comma isn't essential, but semicolon doesn't seem right. Maybe colon?
Go 1 boilerplate is 20 characters on 3 lines:
if err != nil {\n\t\n}\n
This boilerplate is 9 characters on 1 line:
on err, \n
Specifics
The expression is constrained to ensure that its use (if any) within the statement is correct. For example, expression cannot be a function call or channel receive. It could be limited to an lvalue, literal, or constant,
Possible Extensions
For an assignment or short declaration with a single lvalue,
on
could test that value. (Note, I don't recommend this; better to haveon err
consistently on its own line.)An error inspection feature would be helpful...
A condition list suggests parentheses:
Critiques
The above may be too general-purpose. A post-assignment alternative is:
This doesn't accommodate narrowly scoped error variables
if err := f(); err != nil { ... }
. A way to scope the variable to the stmt is:go fmt could just allow single-line if statements:
Would it also allow single-line
case, for, else, var ()
? I'd like them all, please ;-)The Go team has turned aside many requests for single-line error checks.
It's as repetitive as the current idiom:
on err, return err
statements may be repetitive, but they're explicit, terse, and clear.@gopherbot add Go2, LanguageChange
The text was updated successfully, but these errors were encountered: