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: Use ?<variable> simplify handling of multiple-return-values #33074

Closed
gorexlv opened this issue Jul 12, 2019 · 10 comments
Closed
Labels
error-handling Language & library change proposals that are about error handling. FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Milestone

Comments

@gorexlv
Copy link

gorexlv commented Jul 12, 2019

I think the essence of simplifying error handling is to simplifying the handling of multi-return-value.

For example:

// Original 
r, err := os.Open(src)
if err != nil {
   return err
}

// Simplified. 
r := os.Open(src) ?err return err

// If You want to Handle error
r := os.Open(src) ?err handle(err)

// more complicated handling
r := os.Open(src) ?err { handle(err); return err}

// Original
major, minor, ok := http.ParseHTTPVersion("http1.1") 
if !ok {
    os.Exit(1)
}
// To
major, minor := http.ParseHTTPVersion("http1.1") !ok os.Exit(1)

here, importing two special flag: !var and ?var
! means:
assert(var != (zero value) )
if failed, then execute the short circuit logic.

? means:
assert(var == (zero value) )
if failed, then execute the short circuit logic.

	_, ok := imap[key]
       if !ok { 
            imap[key] = make(map[string]interface{}) 
       }
      // ==>
      imap[key] !ok { imap[key] = make(map[string]interface{})}

      if _, err := fn1(); err != nil { return nil, err }
      // ==>
      tt := fn1() ?err return nil, err

       if _, ok := fn2(); !ok { return }
       // ==>
       fn2() !ok return

      for _, s := range []string{...} {
		ret, err := do(s)
               if err != nil { break}
               do2(ret)
	}
       // ==>
       for _, s := range []string{...} {
              ret := do(s) ?err break
              do2(ret)
       }

One of the most common example:

func CopyFile(src, dst string) error {
	r := os.Open(src) ?err return err
	defer r.Close()
	w := os.Create(dst) ?err return err
	defer w.Close()
	io.Copy(w, r) ?err return os.Remove(dst)
	w.Close() ?err return err
}
@gopherbot gopherbot added this to the Proposal milestone Jul 12, 2019
@UFOXD
Copy link

UFOXD commented Jul 12, 2019

please leave the "if err != nil" alone !

@ianlancetaylor ianlancetaylor added v2 An incompatible library change LanguageChange Suggested changes to the Go language labels Jul 12, 2019
@ianlancetaylor
Copy link
Contributor

Similar to #33029.

@ianlancetaylor
Copy link
Contributor

I don't get this one:

major, minor := http.ParseHTTPVersion("http1.1") ?ok os.Exit(1)

In the examples with err ?err means something like "run the next statement if err != nil". In this example, it seems to mean "run the next statement if !ok". That is, for err it runs the statement if the last result is not the zero value of its type, but for ok it runs the statement if the last result is the zero value of its type. What is the exact rule here?

@gorexlv
Copy link
Author

gorexlv commented Jul 12, 2019

I don't get this one:

major, minor := http.ParseHTTPVersion("http1.1") ?ok os.Exit(1)

In the examples with err ?err means something like "run the next statement if err != nil". In this example, it seems to mean "run the next statement if !ok". That is, for err it runs the statement if the last result is not the zero value of its type, but for ok it runs the statement if the last result is the zero value of its type. What is the exact rule here?

@ianlancetaylor
Sorry, actually, here is !ok,
! means:
assert(var != )
if failed, then execute the short circuit logic.

? means:
assert(var == )
if failed, then execute the short circuit logic.

major, minor := http.ParseHTTPVersion("http1.1") !ok os.Exit(1)

@gorexlv
Copy link
Author

gorexlv commented Jul 12, 2019

@AllenYN

please leave the "if err != nil" alone !

There are a number of error handling proposals out there, such as check/try/catch, that are heavily modified at the language level or look ugly. I think the error returned by each expression should be taken seriously and should not be changed, but there is a real need to reduce if err != nil { }, It's too ugly, or there wouldn't be so much controversy😀

@gorexlv
Copy link
Author

gorexlv commented Jul 12, 2019

@ianlancetaylor
yes, it's similar to #33029. But I want to solve the problem from the perspective of optimizing multi-return value processing. Generally, when multiple values are returned, the last one usually carries the control information. when !ok or err != nil, the workflow is usually break or fix. so maybe it's not bad for adding some syntax sugar for quick break or fix without affecting the main workflow.

@mvndaai
Copy link

mvndaai commented Jul 18, 2019

I love that the err variable doesn't need to live outside of when it should be handled.

I am concerned that inlining the handling would make it harder to know the flow of a function.
Would you be okay with gofmt formatting it like this?

r := os.Open(src) ?err {
    return err
}

Which would keep the ability to ignore indented code on a first pass of reading a function. See Mat Ryer's presentation on why he avoids else.

@donaldnevermore
Copy link

The problem is not about err != nil. The problem is about error handling. The ?err is not clear as err != nil.

@gorexlv
Copy link
Author

gorexlv commented Jul 24, 2019

r := os.Open(src) ?err {
    return err
}

@mvndaai yeah, maybe it looks confusing.
But I think the key of ?<variable> is simplify handling of multiple-return-values. The statements behinds ?<variable> I call it short-circuit-logic which is usually break or fix current workflow, so it could be ok if only supporting one single (line) function, like:

// return err
r := os.Open(src) ?err return nil, err
// handle err
r := os.Open(src) ?err handle(err)
// handle then return
r := os.Open(src) ?err return nil, handle(err)
// example
io.Copy(w, r) ?err return nil, os.Remove(dst)

@ianlancetaylor
Copy link
Contributor

This is basically #33029 with a slightly different syntax. This doesn't seem to bring any new functionality beyond that issue. This does not have strong support. Therefore, this is a likely decline. Leaving open for four weeks for further comments.

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. FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Projects
None yet
Development

No branches or pull requests

7 participants