Description
The proposal:
The overview of the error handling problem described here is spot on. The verbosity of error handling can make it tough to spot bugs. However, the design didn't allow for much flexibility in that the check used the most recent, in-scope handle. That doesn't provide a lot of flexibility to users. We can do better, by treating handle as a function (one that either takes a single error argument, or variadic args with the requirement the last arg is the error to check).
I propose we introduce 2 new keywords, check
and with
:
When we check a return value, we can specify how we want to handle it, like so:
func myFunc() error {
check doSomething() // return if error
check doSomething with func(err error) {return fmt.Errorf("error doing something %w", err)}
}
More information and examples in the template below
Would you consider yourself a novice, intermediate, or experienced Go programmer?
5+ YOE with Go in production/enterprise
What other languages do you have experience with?
Java, C, python, dart/typescript/javascript,
Would this change make Go easier or harder to learn, and why?
Easier, minimal new complexity, while readability is greatly increased
Has this idea, or one like it, been proposed before?
Yes, many error proposals exist
If so, how does this proposal differ?
At this point, all of the various error proposals are slightly nuanced. I believe this proposal offers better flexibility in specifying the error handler, while maintaining clear readability.
Is this change backward compatible?
Adds new keywords
Show example code before and after the change.
See the example from the this previous draft's overview for the before.
After:
func formattedError(fmtStr string) func(err error) error {
return func(err error) error{
return fmt.Errorf(fmtStr, err)
}
}
func CopyFile(src, dst string) (err error) {
handler := formattedError(fmt.Sprintf("copy %s %s: %w", src, dst))
r := check os.Open(src) with handler
defer r.Close() with handler // could alternatively call formattedError directly.
w := check os.Create(dst)
defer w.Close()
check io.Copy(w, r) with func(err error) error {return os.Remove(dst)}
check w.Close() with func(err error) error { return os.Remove(dst)}
return nil
}
How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
anything that parses go code.
What is the compile time cost?
Likely minimal. Not sure if this would cause issues with unbounded lookahead though
What is the run time cost?
Depends on implementation/negligible
How would the language spec change?
As described
Orthogonality:
how does this change interact or overlap with existing features?
check would look similar to a return. Take some time to wrap our heads around that, but a welcome improvement to the current 3-liner.
Is the goal of this change a performance improvement?
No
Does this affect error handling?
Yup!
If so, how does this differ from previous error handling proposals?
Again differences are nuanced but a cleaner syntax (IMO), and an expressive with check
that allows flexibility in determining the handle function.
Is this about
generics?
No