-
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: Go2: check
and catch
as built-in macros
#32968
Comments
Instead of built in macros, I think we need hygienic macros in go, as described in #32620, so go developers can experiment with macros themselves, and use the ones they like themselves in their own projects. |
Ah, wouldn't it be a wonderful change?! Giving to every person ability to make its own flavor of Go? It would also enable readers to feel again this peculiar joy of jumping from file to file, to wonder the beauty of the macros and to adore the genius of their creators... https://docs.racket-lang.org/reference/ |
This has the same problems as try/catch in other languages, namely that you wrap a whole bunch of lines in a handler and then the handler code can't know what part failed. It discourages people from handling every error individually. And just like exception in other languages, (which is actually worse than the |
check
and the catch
as a built-in macro proposal. For errors checking and more.check
and catch
as built-in macros
CC @griesemer |
@ohir Built-ins in Go have a clearly specified syntax: Syntactically, they look like function calls with expressions as arguments. There may be additional rules for the arguments, but they remain expressions. There may be special behavior to the built-ins, but syntactically they remain function calls. What you are proposing does not look like a function call, nor an expression: there's a In order to make something like What you are proposing is a new form of statement for Go; a For instance, you say that In short, I don't believe this proposal can be implemented in any reasonable way. It is also not very Go like as it introduces invisible code to be executed upon assignments. More importantly, you are proposing a fairly general new mechanism with all kinds of ramifications without an argument as to why this is needed. Certainly, error handling can be done with less effort. I am against this proposal. |
@natefinch Thank you. It is a valid concern about possible abuse of this construct. Though I do (I might) know exactly which what part failed from the err (or other variable being checked) if I am really interested in it. |
It needs not to be detected. The Though this exposed the vagueness of the "matching" definition I gave. After correction it should say:
As it is a macro, it is expanded, it allow any and all expressions that have a bool result. Ie. any expression that can be parsed in the expanded The "Implementation" section of the proposal is straight. Where it says "insert
There is no magic in check(err != nil || rv == nil)
{
rv, err := fn()
continue retry
} will result in same AST as {
rv, err := fn()
if err != nil || rv == nil {
goto catchˁ1
}
goto okˁ1
catchˁ1:
continue retry
okˁ1:
} ([1] same AST spare flag annotations on nodes
I am pleased to inform you that
I do. The 'Implementation' part is the implementation sketch. The More work will be with the fmt printer. All other go tools will stay untouched, including debuggers, where the real identifiers like
With all due respect, the
they should also be trusted to learn and understand "the
Agreed, its new, but it reads Go:
I will not beat around the bush: the real impulse to spending a week on thinking about simplest solution to 'if err != nil' whinning, and one that will benefit the most of users was my deep concern about The
Yes, indeed. Though |
Thanks @ohir for your explanations. So what happens if the condition in
when is the check As you say, any and all expressions can be used with
where
What if Also, this construct would be the first construct where we can refer to an identifier, This proposal seems to be open many more questions and problems than it seeks to solve. |
check(a == b) {
a := f1()
b := f2()
}
With this example? Never. It is an invalid code and effects in a compile error:
|
Thanks for the explanations. Contrary to what you say, As I said before, this proposal seems to open more questions and problems than it tries to solve, with little motivation as to why one would want such a mechanism. |
@ohir You seem to be saying that |
@ianlancetaylor This is a real flaw that should either be addressed or excluded. Thank you. Edit: added Limitations section to the spec.
But - again - I personally would like users of a macro to know they are using a macro and how does this macro expands. |
Update. Added Limitations section. Rationale: A macro may not change language specs. Even if #26058 would loosen |
With just added (natural) limitation the whole bunch of lines should be fairly limited. The potential to gross abuse you pointed at, as in enclosing in
The "must acquire with retries" pattern I have shown is the bread and butter of microservice architectures (and many "bigger-services" too). As it happens "cloud" backend is the domain where Go has most applications. |
This proposal as it stands relies on simple macro expansion. That means that we can't use All of these issues could perhaps be addressed. But we would still have a Go statement that defines a condition that is then checked dynamically during execution of the block. There is nothing else in Go that acts that way. We would have to clearly define exactly when the checks occur. We also have to specify what kinds of There does not seem to be much enthusiasm for this proposal. -- writing for @golang/proposal-review |
@ianlancetaylor |
The
check
and thecatch
revisited as a macroThe implicit version of the
check
already compiles at playground.This proposal is not a direct counter-proposal to #32437. It stands by itself.
The
check
macro is not a one-liner: it helps the most where many repetitivechecks using the same expression should be performed in close proximity.
Recent use of the
catch
word was spotted in: #32813 and #32611.Design constraints (met)
It is a built-in, it does not nest in a single line, it allows for way more flows than
try
and has no expectations about the shape of a code within. It does not encourage naked returns.usage example
func check(Condition bool) {}
built-in macro inserts a check for theCondition
after any matching statement in the anonymous{ block }
that immediately follows the macro invocation.identifier
used in the "Condition" expression of thecheck
appears on the left to the "=" or ":=" assignment operator within the block.true
, statements under the "catch:" label inside theblock
are executed immediately.Implementation uses none magic:
check
macro was invoked. ThencatchˁLN
andokˁLN
labels are used.; if Condition { goto catchˁLN };
is put in the place of the single 'invisible semicolon'.catch:
label is spotted (parsed), it gets replaced with agoto okˁLN;
statement followed by acatchˁLN:
label.catch:
label yet, this statement gets prepended with thegoto okˁLN; catchˁLN:
.okˁLN:
label is put right before the end of the block.check context
annotation for AST tools (gofmt, gopls and others).Limitations
Variables local to the block must be declared with
var
at top of the block.This is a direct consequence of keeping to 'no-magic' principle.
Go's sane
goto
usage safeguards should not be circumvented.Thats all spec.
Macro expansion explained:
changes:
-"Matching" are statements that assign a value to at least one of variables that are checked in the "Condition" expression
+"Matching" are statements where a variable
identifier
used in the "Condition" expression of thecheck
appears on the left to the "=" or ":=" assignment operator within the block.+"Limitations" section.
The text was updated successfully, but these errors were encountered: