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: add tryfunc keyword to further reduce boilerplating for error handling #32964

Open
sougou opened this issue Jul 6, 2019 · 4 comments

Comments

@sougou
Copy link
Contributor

commented Jul 6, 2019

First of all, the try approach in #32437 is a huge improvement to what we have now. So, I'll be happy if that moved forward. The following proposal is slightly more radical.

Summary: Introduce tryfunc that removes all error handling boilerplate within a function.

Original:

func f1(in int) (out int, err error) {
  val, err := f2()
  if err != nil {
    return 0, err
  }
  if err := f3(); err != nil {
    return 0, err
  }
  if err := f4(); err != nil {
    return 0, fmt.Errorf("f4 failed: %v", err)
  }
  return f5()
}

New:

tryfunc f1(in int) int {
  val := f2()
  f3()
  if err := f4(); err != nil {
    return 0, fmt.Errorf("f4 failed: %v", err)
  }
  return f5()
}
  • A tryfunc implicitly adds an (unnamed?) error return parameter to the function.
  • An invocation f() that returns an error within a tryfunc will be equivalent to try(f()).
  • A function that does not return an error will be as if it was invoked without the try.
  • Just like the _, ok construct, one can explicitly request the error from a function, and that will automatically disable the error handling for that call.
  • A closure within a tryfunc can be a func or a tryfunc.

@gopherbot gopherbot added this to the Proposal milestone Jul 6, 2019

@gopherbot gopherbot added the Proposal label Jul 6, 2019

@sougou sougou changed the title Proposal: Go 2: further reduce syntactic sugar for error handling Proposal: Go 2: further reduce boilerplating for error handling Jul 6, 2019

@mccolljr

This comment has been minimized.

Copy link

commented Jul 7, 2019

I see lots of 👎 reactions and no comments so I'll at least offer my reasoning for the 👎. There's a lot of magic that happens in a tryfunc, and it takes away a lot of the explicit clarity go code usually provides. It's difficult to know at a glance which function calls within the function can cause errors and which can't. Plus, if the signature of one of those functions changes to add an error return, your tryfunc behavior will change silently. In your small example these problems aren't such a big deal, but more complex real-world code I would expect both these things to cause real bugs and real confusion.

@deanveloper

This comment has been minimized.

Copy link

commented Jul 8, 2019

I'll add a comment here about why there may be so many 👎 reactions, I recently added mine and I usually don't like to 👎 without a comment. Explicit error handling is important, if an error ever happens, you want to have some way of knowing which lines could have possibly caused it. For instance, take a look at this Java code:

public class Main {
    public static void main() {
        try {
            doStuff()
        } catch (Exception e) {
            System.out.println("An error occurred!")
        }
    }

    public static void doStuff() {
         callA()
         callB()
         // ....
         callZ()
    }
    // Implementations of callA, callB, etc...
}

If an error occurs, there is no way to figure out which calls could have even possibly thrown an error. Some of callA, callB, etc may throw errors, but not all of them.

The same thing in Go, with try:

import "fmt"

func main() {
    err := doStuff()
    if err != nil {
        fmt.Println("An error occurred!")
    }
}

func doStuff() error {
    callA()
    try(callB())
    callC()
    callD()
    // ...
    try(callZ())
}

// Implementations of callA, callB, etc...

This time, we can tell which functions might have thrown the error. It doesn't look quite as useful in my example, but in real-life applications it makes a huge difference to be able to know which functions may return an error.

What this proposal does is allow changing the doStuff function to look like:

tryfunc doStuff() {
    callA()
    callB()
    callC()
    callD()
    // ...
    callZ()
}

This doesn't annotate which functions are returning errors, nor does it even show itself returning an error (when in fact it does). Not to mention that a tryfunc is essentially just a func, and Go doesn't like non-orthogonal features 😉

@sougou

This comment has been minimized.

Copy link
Contributor Author

commented Jul 8, 2019

I was just trying to think outside the func :). In any case, this is a radical proposal, and for all you know, it may not be worth it. But here's the premise:

The influence comes from the type of systems I mostly work with: distributed systems where you are called from a remote system, and have to eventually call out into an external service, which can fail. In these situations:

  • Almost every function must return an error.
  • Almost every invocation has to check and return the error up the stack, eventually to the remote caller.

If all functions in a program were tryfuncs, then it's not magic; It's understood that any function can return an error, and if so, it's guaranteed to be sent up the stack.

However, I can think of one really bad case: if someone changes a tryfunc to a func, then all errors will fall on the floor.

@ianlancetaylor ianlancetaylor changed the title Proposal: Go 2: further reduce boilerplating for error handling proposal: Go 2: add tryfunc keyword to further reduce boilerplating for error handling Jul 17, 2019

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Sep 17, 2019

This proposal does not have much support. Some cogent objections were expressed above, and we agree with them. Therefore, this is a likely decline. Leaving open for one month for further discussion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.