Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.Sign up
proposal: Go 2: aggregate error handling & defer syntax sugar #34403
Aggregate error handling
I re-open an issue #34140 I myself closed a week ago because it seemingly stuck in a dead-end; Now, I have a solution.
A lot of people already tried to raise a question to improve close-to-perfect error handling in Go in one way or another, so I apologize twice that I do it not just once again, but a second time in a month. And still. I am quite new to Go, started using it very intensely just recently, and there is this thing that so annoys me, that I literally sleep and dream what can be done to that.
Context and The Problem
I totally agree to the consensus that it's already good as it is now. However, there's this quite typical case, where the same code is typed all over again, polluting the screen, and making what otherwise would fit in half the screen, to span two screens. Here it is:
As you can see, the same thing is repeatedly typed again and again. And it's not just typing, it also consumes valuable space on the screen, and disperses the focus of attention.
Now, don't you see the pattern? Hope yes.
In my previous proposal #34140 I suggested this syntax idiom to solve this:
And suggested to implemented it with
The above example of opening and reading
Note, this actually compiles and works. And it solves all the problems outlined previously, including the scope shadowing, and limitations of
So what is left now, is to add some syntactical sugar to it.
Let's re-format it a little bit:
It's still essentially the same code, it compiles and works.
Now, let's make these substitutions "by preprocessor":
Now we can write:
That is it.
And it doesn't require any changes to the language inner workings, only this syntactic sugar.
This all is possible at these expenses:
Examples of how it might look like
I will basically take the (anti-)examples from the beginning of this proposal, and re-write it in a new syntax, one-by-one:
Imagine how it would affect the code, if there are not two, but (as it is typically) 4..10 of them. You don't need to imagine, you can see it here, how much shorter and more readable the code becomes.
And one more thing.
One might argue, that "all this is unnecessary, we love old good ways to do things". And I will answer by pointing him/her at the fact that Go already has quite a lot of various syntactic sugar, long present in Go,
So please vote it up.
IF ALSO REPLACE THIS:
(By "replace" I mean making it syntactically equivalent)
THEN the code example from Stackoverflow - How to read/write from/to file using Go? can be rewritten this way, please look at the picture:
Not just a syntactically equivalent, but actually apply
Id est this:
would unfold into this:
But that should be a different proposal, then.
Where "bs" stands for "block-scope", not what might one think else.
There's yet another way to solve this in existing Go. This code:
It will print:
In this, there's no problem with goto jumping over var definitions, no problem with scope, no nothing.... Except, it's ugly. It reminds me of Perl way of handling exceptions, which is:
The point is, it is already in the language, but its very existence is not obvious nor it is concise and clear in code what it is.
The above solution has these advantages (relative to the original solution of this proposal):
And these disadvantages:
It's possible to add syntax sugar to it. Let's re-format the myf1() and main() functions:
Now, if replace these (just like in original proposal):
The "b" stands either for 'body' (as in Ada) or 'begin', as who likes how.
Then there'd be no longer checkErr() calls, and we can re-write the code this way:
In the end, it looks like in Go there are two alternative mechanisms to handle errors, panic()/recover()/defer(), and
P.S. few hours later.
The translation will happen as part of a build process.
But there's more to it. It's not about handling errors, but is directly related to that.
What if I want to re-try an operation several times, probably with a delay, until finally failing? What if I want to ask an operator (read: user) if it should be re-tried once more?
There are certain fields, e.g. a distributed industrial IO (or distributed anything, when a connection can be spotty, but the operation to be performed is VERY IMPORTANT). For example, there is a fire on the floor plant, and some communications are broke, some are not, and mostly the field network work at 75% of data loss, and the safety logic requires that in such a situation that particular pump should be tried to be switched on no matter what. Or, if that's not possible, then an alternative action must be performed.
This can be performed with this idiom:
However, it could've been written this way, much clearer, with the expense of introducing a
However, it looks not that nice, and isn't possible, because there's no way to re-invoke passed in function several times, with the same arguments pre-evaluated beforehand. Just like defer does, it first evaluates the parameters, and puts a ready-to-be-called record of a function call on the stack. There's no generally usable mechanism for that, and I am not sure if it's needed. Well, if it was present, then there would be possible (probably) to implement user-defined
Let's see what it would look like, hypothetically. Let's create a special new type, 'immutable, input parameters-evaluated, function call", "iipefc", and then:
Now we could pass
However, it's not clear how
The original idea of:
doesn't suffer from either of these problems.
Ironically, none of this could ever go into Golang for the very same reasons why the error handling in Go is still sane and simple. Namely, because of conservativeness of its designers (if I am right), and their drive to simplicity and "old-good ways" of doing things (C) and granularity of control.
But, the existing error handling in Go isn't perfect, and here's why. I did some research on the Internet, and there are plenty of attempts over the years to offer some kind of improvement to it, and very few to improve, say, channels. That's an indicator, I think. An indicator that channels in Go are designed perfectly.
Hi, this is an "error-handling guy" again, with a little feedback from myself.
I actually tried to implement it in an actual production project, and it worked and works just nicely.
However, I stumbled upon the following observation / wanted feature: I got too many points of possible issuance of an error, with
One way to create messages (or wrap passed up errors with additional message) was in
And it works. But I also wanted that these also do the wrapping, for example on the calling side of Transact():
So here it is - I have two
So, here's what I did:
This is my "pre-processor", written in Perl, which does the transformation on each line of source code of Go program. The line I marked, basically transforms this, e.g.:
thus automatically wrapping an error.
In short, here's an example of production code that is possible with these improvements:
and so on and on. It's like a stack trace, but differently formatted, and as convenient, probably more.
This is an example a real-life error reported, as seen on the terminal:
Which means that PostgreSQL erred with "pq: column pd.points_to_id does not exist", at "Querying tbl_id_list failed", at main. Debugging becomes a no brainer. The code looks clean and tidy.
As long as this is not part of the Go language, it has this limitation:
This works. But, unfortunately, there's no way I can add
Similar proposals, such as #33002, have been discussed at length before. See those issues for more discussion. Any new proposal in this area should consider those earlier issues and offer something new and distinctively better. Also, this specific proposal does not seem to have aroused much interest in the community. For these reasons, this is a likely decline. Leaving open for four weeks for further comments.
I understand you will close it after 4 weeks.
However, I would note, that it's not just another "error handling proposal", like the one you mentioned, the #33002. I actually went a long way comparing what was proposed before.
The fundamental difference of this particular proposal from the one you mentioned, the #33002, is that that is a syntax just-a-proposal, and this one offers a solution how to achieve that, and also this one really works, at least in one project. I demonstrated that to make it work all is needed is a per-line regex translation of source code, even with zero modifications to the Go compiler.
It is convenient.
You are free to close it. Because you all are tired of steady flow of proposals to improve error handling, and because no one cared to support this one.