Skip to content
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: Error handling via conditional assignment operators #59664

Closed
zupa-hu opened this issue Apr 17, 2023 · 12 comments
Closed

proposal: Go 2: Error handling via conditional assignment operators #59664

zupa-hu opened this issue Apr 17, 2023 · 12 comments
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@zupa-hu
Copy link

zupa-hu commented Apr 17, 2023

Author background

  • Would you consider yourself a novice, intermediate, or experienced Go programmer?
    Experienced.
  • What other languages do you have experience with?
    TypeScript, PHP, C, C++, Java, Rust, Haskell, Pascal, Basic, Assembly.

Related proposals

  • Has this idea, or one like it, been proposed before?
    There has been countless error handling proposals of course but I'm not aware of any that has introduced new assignment operators.
    • If so, how does this proposal differ?
      It introduces conditional assignment operators that either assigns the success values or returns the error value.
  • Does this affect error handling?
    Yes.
    • If so, how does this differ from previous error handling proposals?
      It introduces new assignment operators.
  • Is this about generics?
    No.

Proposal

  • What is the proposed change?
    • In layman terms: introduce a new assignment operator =. that
      • treats the last to-be-assigned value as the error
      • if the error is non-nil, return it as the last argument
      • otherwise assign all but the last value to variables on the left.
    • As this operator would be used a lot, it should add minimal clutter. Possibly =. but the actual syntax could be different.
    • I'm proposing a suffix form so that it can be used with short variable declarations as well (:=.).
    • The suffix form could be used with all other assignment operators as well. (+=., etc..). That's not really important though, it just follows from the pattern.
  • Who does this proposal help, and why?
    • Makes the code more readable by reducing if err != nil { ... } checks.
    • Increases coding velocity by reducing boilerplate.
    • It does not help with adding debug information to errors. Those can still be added with defer. This proposal explicitly addresses the "desire path" of avoiding if err != nil { ... } checks in case one would not add debug information anyway.
  • Please describe as precisely as possible the change to the language.
    • Introduce the new conditional assignment operator =..
    • Upon conditional assignment:
      • if the last to-be-assigned value is non-nil, the current function's last return value is set to this value and the current function returns.
      • otherwise, all but the last values are assigned to variables on the left hand side.
    • Similarly for all other conditional assignment operators like :=. and optionally +=., &=., etc..
  • What would change in the language spec?
    • The new operators =. and :=. would be added.
    • The new operators would be added in the Terminating statements section as conditional terminating statements.
    • Optionally, conditional assignment could be added to all other assignment operators, also introducing +=. and &=. etc..
  • Please also describe the change informally, as in a class teaching Go.
    If you just want to return on error, you can use the conditional assignment operator to simplify your code.
  • Is this change backward compatible?
    Yes.
    • Before
      a, err := A()
      if err != nil {
          return nil, err
      }
      a, err = A()
      if err != nil {
          return nil, err
      }
    • After
      a :=. A()
      a =. A()
  • Orthogonality: how does this change interact or overlap with existing features?
    I believe it is orthogonal. The benefit of using a suffix after the assignment char instead of a prefix is that it could be supported with all other assignment operators that are prefixes, though that is not crucial.
  • Is the goal of this change a performance improvement?
    No.

Costs

  • Would this change make Go easier or harder to learn, and why?
    It probably has a net positive impact. One additional feature to learn in exchange for code that's easier to read and faster to write.
  • What is the cost of this proposal? (Every language change has a cost).
    I'm not sure about the scope of this question. Please clarify.
  • How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
    Probably most.
  • What is the compile time cost?
    I would expect it to be negligible.
  • What is the run time cost?
    Zero as it compiles to the same underlying code one would write without this feature.
  • Can you describe a possible implementation?
    I'm not as deeply familiar with the Go codebase.
  • Do you have a prototype? (This is not required.)
    No.
@gopherbot gopherbot added this to the Proposal milestone Apr 17, 2023
@zupa-hu
Copy link
Author

zupa-hu commented Apr 17, 2023

@ianlancetaylor thanks for your work on managing the issues here. If you think this proposal has no likelihood of being accepted, please go ahead and close it to save everyone's time.

As said I could not find a proposal that was 'close enough' to be a reference point and I thought the subtleties of this proposal were meaningful enough to be considered a novel avenue.

@earthboundkid
Copy link
Contributor

If it's an assignment operator, it can't work with func() error, can it? It also doesn't seem to have a good way of annotating an error.

@seankhliao seankhliao added LanguageChange v2 A language change or incompatible library change error-handling Language & library change proposals that are about error handling. labels Apr 17, 2023
@ianlancetaylor
Copy link
Contributor

The effect is kind of like the try proposal (#32437) except that instead of writing a = try F() one would write a =. F(). I'm not sure that replacing try with a period makes it much better. A period is rather small and seems easy to miss when reading code.

@zupa-hu
Copy link
Author

zupa-hu commented Apr 17, 2023

If it's an assignment operator, it can't work with func() error, can it?

Why not? It makes sense to disallow = on its own as it doesn't make sense, but =. does.

=. A()

It's does look weird at first but it also does its job. Return on error or result in void.

It also doesn't seem to have a good way of annotating an error.

True.

@zupa-hu
Copy link
Author

zupa-hu commented Apr 17, 2023

@ianlancetaylor
Differences to the try proposal:

  • I understand the try proposal landed on a built-in function as adding a try operator would not be backwards compatible.
  • A built-in function adds more visual 'noise'.
  • To have a built-in function change the control flow is very unexpected.

Otherwise they indeed have the same effect.

My primary intention was to bring up the idea that an extra assignment operator could also be used to reduce repetitive if-err checks as I have not seen it discussed. I feel like the actual chars used in the operator are secondary to how it works.

@ianlancetaylor
Copy link
Contributor

My primary intention was to bring up the idea that an extra assignment operator could also be used to reduce repetitive if-err checks as I have not seen it discussed.

I agree that I can't recall seeing a proposal quite like this.

I feel like the actual chars used in the operator are secondary to how it works.

I don't agree here. When people discuss the problem of error handling in Go, they can mean several different things, but one of them is a complaint about the syntax. This proposal, like many other error handling proposals, is about reducing the syntactic boilerplate for error handling. As such, the syntax is really the critical part. The syntax we all use today is verbose but clear. When we talk about new syntax, it will presumably be less verbose, but it must still be clear. We shouldn't say that the syntax is secondary. It's really the whole point of the proposal.

@godcong
Copy link

godcong commented Apr 18, 2023

In that case why not add the concept of aliases
Aliases would be embedded directly in the code instead of generating functions

aliase ErrRet(e error) {
    if e ! =nil {
         return e
    }
}

aliase ErrRet2(file io.Reader, e error) {
    ErrRet(e)
    //file rw from f
}

func useExample() error {
   f, err := os.Open(filename)
   ErrRet(err)
   // file rw from f
}

func useExample2() error {
   ErrRet2(os.Open(filename))  
}

func useExample3() error {
   f, err := os.Open(filename)
   if err ! =nil {
         return e
    }
   //file rw from f
}

This is like other languages trait in use, perhaps also called trait.
But I added the type definition e error, so that different error names can be associated in different codes.
The final code will be compiled as useExample3 .
useExample2 is a more complex nested usage, but the final result is like useExample3,
But this is to prevent loop nesting.

This does not affect the previous implementation, but also can streamline a lot of code.
Of course this is not just for error handling, it can be used for other things as well.
If you have a large chunk of code that is the same.

It might be possible to use, it's a reference to the existing alias definitions, but of course there might be some differences:

type ErrRet = (e error) {
   //do some error checking
}

@ianlancetaylor
Copy link
Contributor

@godcong That is a completely different idea. Let's please keep this proposal focused on the idea suggested in the original comment. Thanks.

(The idea you mention is along the lines of #57822. But still, let's not discuss it here. Thanks.)

@zupa-hu
Copy link
Author

zupa-hu commented Apr 18, 2023

We shouldn't say that the syntax is secondary. It's really the whole point of the proposal.

What I meant is that the chars of the operator could change if the functionality is otherwise desirable. But you are right, I proposed this because that was the best I could come up with and it's not like we could just improve it later.

@zupa-hu
Copy link
Author

zupa-hu commented Apr 18, 2023

With a fresh mind, I'd like to reverse my response to @carlmjohnson . He is right, this is totally confusing.

@ianlancetaylor is it okay if I close this?

@seankhliao
Copy link
Member

feel free to close it if you no longer think it's a good idea

@zupa-hu
Copy link
Author

zupa-hu commented Apr 18, 2023

Thank you all for your time, I'd like to withdraw the proposal.

@zupa-hu zupa-hu closed this as not planned Won't fix, can't repro, duplicate, stale Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

6 participants