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: introduce "then" keyword in if statement to allow one liner if statement for better error handling. #46717

Closed
ibudisteanu opened this issue Jun 12, 2021 · 23 comments
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Proposal Proposal-FinalCommentPeriod v2 A language change or incompatible library change
Milestone

Comments

@ibudisteanu
Copy link

ibudisteanu commented Jun 12, 2021

Would you consider yourself a novice, intermediate, or experienced Go programmer?
Experienced

What other languages do you have experience with?
C, C++, Javascript, Java, Python, Delphi, Rust

Would this change make Go easier or harder to learn, and why?
I don't think it makes the GO harder to learn. The proposed "then" keyword allows the one line if statement

Has this idea, or one like it, been proposed before?
The solution is similar with the most other error handling proposals. This proposal doesn't add any other code changes

If so, how does this proposal differ?

Who does this proposal help, and why?
1. It helps authors of Go source code avoid three extra lines of code in the common case where they early exit on an error.
2. It helps readers of Go source code follow a function's core logic more easily, by collapsing the gap between two successive lines of logic.
3. Preserves the imperative style of Go source code. Unlike the try proposal, this proposal can't be abused to favor function composition.

What is the proposed change?
1. A new keyword "then" for the if statement is introduced.
2. The keyword is only valid in if statements on the right-hand-side of the if statement allowing one line if statements.
3. After the keyword a statement is required but it can be a block as well.
5. In all other cases, check is a normal identifier. This keeps the change backwards-compatible to Go1.
6. Given a function like this:

func foo() (v1 T, err error) {
  var x T
  if x,  err = f(); err != nil then return
}
is equivalent to the following code:

func foo() (v1 T, err error) {
  var x T
  if x, err = f(); err != nil {
    return nil, err
  }
}

Is this change backward compatible?
Yes. Totally!

Show example code before and after the change.
Before https://play.golang.org/p/bQeCE9zzDPa

func printSum(a, b string) (err error) {
        var x,y int
	if x, err = strconv.Atoi(a); err != nil {
		return
	}
	if y, err = strconv.Atoi(b); err != nil {
		return
	}
	fmt.Println("result:", x + y)
	return nil
}

After using the "then" keyword

func printSum(a, b string) (err error) {
        var x,y int
	if x, err = strconv.Atoi(a); err != nil then return
	if y, err = strconv.Atoi(b); err != nil then return
	fmt.Println("result:", x + y)
	return nil
}

What is the cost of this proposal? (Every language change has a cost).
None. Just a regex parse.

How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
I guess it requires only one regex expression.

What is the compile time cost?
Time-complexity-wise the same. Can be implemented entirely in the frontend.

What is the run time cost?
Nothing

Can you describe a possible implementation?
It is just another regex in the language parser

Does this affect error handling?
Yes

We are working on a go project. We have about 18,000 LOC. about 18% of the LOC are just error handling... Error handling in GO is a nightmare because of the required brackets and code format.

It can also be handy in other parts of the code

func (wallet *Wallet) loadWallet() error {

   if _, err := os.Stat("wallet.txt") ; err == nil {
        if err := wallet.read(); err != nil { 
           return err 
       }
        if wallet.encrypted {
           if err := wallet.decrypt(); err != nil {
             return err
          }
       } 

   } else {
       if err := wallet.createAddress(); err != nil then return err
   }

   if wallet.addresses == 0 {
         errors.new("There are no addresses in the wallet")
   }
   return nil
}
func (wallet *Wallet) loadWallet() error {

   if _, err := os.Stat("wallet.txt") ; err == nil {
        if err := wallet.read(); err != nil then return err
        if wallet.encrypted then
           if err := wallet.decrypt(); err != nil then return err     
   } else then
       if err := wallet.createAddress(); err != nil then return err

   if wallet.addresses == 0 return errors.new("There are no addresses in the wallet")

   return nil
}
@ibudisteanu
Copy link
Author

The proposed solution reduces dramatically the entire code that a developer has to write, as there are many cases when one line if statement is required.

@ibudisteanu ibudisteanu changed the title Go2: introduce "then" keyword in if statement to allow one liner if statement for better error handling. proposal: Go2: introduce "then" keyword in if statement to allow one liner if statement for better error handling. Jun 12, 2021
@gopherbot gopherbot added this to the Proposal milestone Jun 12, 2021
@davecheney
Copy link
Contributor

What if we fixed go fmt so you could write

if x, err = strconv.Atoi(a); err != nil { return }

It's two characters shorter than than, if that's important.

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 12, 2021

The issue i have is that the IDE i am using is automatically formatting all the if statements and is also folding by expanding the blocks most of the time. The issue I believe is the number of lines of code rather the 2 extra characters. Ideally would be "?"

@davecheney
Copy link
Contributor

Right, but if we’re talking bout chasing the language, something on the table for comparison should be changing go fmt to permit trivial if blocks to stay on the same line.

@jfesler
Copy link

jfesler commented Jun 12, 2021

Whatever change is made (if at all), the IDE will have to change. So arguments about the IDE doing it wrong are moot.

@ibudisteanu
Copy link
Author

I was just pointing that I believe most people including me think GO doesn't have proper ways to handle errors is mostly because GO requires brackets in if statements and IDEs are automatically expanding these blocks most of the time.

@seankhliao seankhliao added error-handling Language & library change proposals that are about error handling. v2 A language change or incompatible library change LanguageChange labels Jun 12, 2021
@seankhliao
Copy link
Member

I don't see how this is significantly different from a single line if, other than being more complex #27135 and others

@ianlancetaylor ianlancetaylor changed the title proposal: Go2: introduce "then" keyword in if statement to allow one liner if statement for better error handling. proposal: Go 2: introduce "then" keyword in if statement to allow one liner if statement for better error handling. Jun 12, 2021
@ianlancetaylor
Copy link
Contributor

This has some similarities to #38151. Also to #32946.

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 12, 2021

My proposal was that using the proposed keyword "then" you can solve most error handling in a single line. Also for integrating this proposal I believe it requires adding one single regex expression for parsing the word "then" and expecting an instruction.

Most likely the community will fork golang and create a golang compiler that allows one liner as you guys simply don't want this. Something like a simpler version of Go+

@urandom
Copy link

urandom commented Jun 13, 2021

@davecheney

You can't. That would cause huge swaths of unnecessary changes for any commit

@b97tsk
Copy link

b97tsk commented Jun 16, 2021

Maybe change gofmt to allow following to stay on the same line:

if err != nil {{ return }}

Or when there is a comment:

if err != nil { return } //gofmt:inline

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 16, 2021

how about
if err != nil then return

@89z everybody hates the golang 3 liner error handling in big projects

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 16, 2021

I wrote about 16k LOC in golang in the last 2 months. I enjoy the language but the error handling is extremely verbose. I even looked for golang transpiler on the web. I found this https://github.com/lunixbochs/og

https://github.com/PandoraPay/go-pandora-pay

Regarding your examples, the "then' keyword would solve most of the issues above. Check this out.

re := regexp.MustCompile("some (pattern)")
if find := re.FindStringSubmatch("some input"); err == nil then  return find[1], nil
else then return "", errors.New("not found")
func blah() (*os.File, error) {
   if f, err := os.Open("something"); err == nil then return f, nil
   else then  nil, fmt.Errorf("call os.Open in func blah: %v", err)
}

Because I find golang very verbose, It makes me believe it is intended for very small projects, like scripts, or small servelet.

LE2: As I mentioned before, doing some code analysis, I found that about 18% of the LOC are the "infamous 3 line error handling". Taking in consideration the 3 line error handling is actually 3 lines it means that about 40% of the code is just error handling ... It means, there is something wrong with the golang three liner error handling

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 16, 2021

@89z i just want to have a one liner error handling. I totally believe that something must be done. I proposed one option "then" keyword. I don't care how it can be done we want one liner error handling :D .

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 16, 2021

i@89z that is not one liner error handling. The IDE I am using is unfolding by expanding these blocks most of the time. Also when committing on github, the LOC are expanded again. As far as I understood it is the gofmt that is doing this.

func two(name string) (f *os.File, err error) {
   if f, err = os.Open(name); err != nil { return }
   return f, nil
}

also this approach with
if f, err = func(); err != nil {return}

it is not that great because in case func() is also returning some value and error, the value is being propagated

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 16, 2021

I don't know how to disable gofmt in goland. If you tell me then I will disable it and I will never activate gofmt again. I only see that you guys received over 50+ proposals in all these years for different one liner error handling and all of them were refused for one reason or another.

To illustrate why gofmt is bad in practice... Just a random thing I was working on right now... and the reason why a real world one liner if statement and error handling is really useful.

select {
case newWork, ok := <-continueProcessingCn:
	if !ok {
		return
	}
	if newWork != nil {
		work = newWork
		listIndex = 0
		txMap = make(map[string]bool)
	}
case _, ok := <-suspendProcessingCn:
	if !ok {
		return
	}
	continue
}
if work == nil {
	continue
}
if len(txList) > 1 {
	sortTxs(txList)
}
select{
case newWork, ok := <- continueProcessingCn:
	if !ok then return
	if newWork != nil {
		work = newWork
		listIndex = 0
		txMap = make(map[string]bool)
	}
}
case _, ok := <- suspendProcessingCn:
	if !ok then return
	continue
}
if work == nil then continue
if len(txList) > 1 then sortTxs(txList)

22 LOC vs 15 LOC. A reduction of 31.81% LOC

@fzipp
Copy link
Contributor

fzipp commented Jun 16, 2021

@ibudisteanu Less lines of code do not mean more readable. You can write the whole program in one line if you insert the semicolons. gofmt was designed under the premise that your 22 LOC version is preferable to the 15 LOC version.

@ibudisteanu
Copy link
Author

ibudisteanu commented Jun 16, 2021

@fzipp I honestly believe that the 2nd option with 30% less lines of code is more readable that the 1st option especially when you have large projects.

@fzipp
Copy link
Contributor

fzipp commented Jun 16, 2021

@ibudisteanu Your variant tucks away what is, in my opinion, the most important information of a function body: where does the control flow change, what are the edge cases.

@ianlancetaylor
Copy link
Contributor

Based on the discussion above, and the emoji voting, this is a likely decline. Leaving open for four weeks for final comments.

@ianlancetaylor
Copy link
Contributor

No further comments.

@hauntedness

This comment was marked as off-topic.

@golang golang locked as resolved and limited conversation to collaborators Jun 29, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
error-handling Language & library change proposals that are about error handling. LanguageChange Proposal Proposal-FinalCommentPeriod v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

11 participants