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

Closed
sougou opened this issue Jul 6, 2019 · 5 comments
Labels
error-handling Language & library change proposals that are about error handling. FrozenDueToAge LanguageChange Proposal Proposal-FinalCommentPeriod v2 A language change or incompatible library change
Milestone

Comments

@sougou
Copy link
Contributor

sougou 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
@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
@agnivade agnivade added v2 A language change or incompatible library change LanguageChange labels Jul 7, 2019
@mccolljr
Copy link

mccolljr 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
Copy link

deanveloper 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
Copy link
Contributor Author

sougou 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
Copy link
Contributor

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.

@ianlancetaylor
Copy link
Contributor

There were no further comments.

@bradfitz bradfitz added the error-handling Language & library change proposals that are about error handling. label Oct 29, 2019
@golang golang locked and limited conversation to collaborators Oct 28, 2020
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 Proposal Proposal-FinalCommentPeriod v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

7 participants