Lately, there has been a lot of talk about the proposal of introducing a new way of handling the
if err != nil { return err }
pattern that seems to be loved by some and hated by others. It will often stir up a flame
war when brought up among gophers. There are countless issues on this topic on golangs github and blogs written about it and I guess this yet another.
The initial proposal was brought forward in 2018 when the content of Go2 was pitched to the community and it can be found
at https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md
As well as the following discussion and feedback at https://github.com/golang/go/wiki/Go2ErrorHandlingFeedback
try
statementThe proposal that recently was backed by the core team was the try
statement which would look something like the following
val, err := someFunc()
if err != nil {
return err
}
//could be written as
val := try(someFunc())
At first glance, this looks like much of what a lot of people have been asking for in go and would more or less be implemented by rewriting the code in compile time to the original way of handling errors.
This proposal was recently rejected due to the feedback from the community and I think this proposal process was handled very well by the core team. Building proof of concept and letting people try it out and find problems and benefits with it.
try
I think it was good that this was rejected. There are a lot of potential issues with the try
statement and I encourage you to read about it.
However, the main problem I saw with it was not the functionality of it but rather the abstract concept of it.
Go is a minimalist language in terms of functionality, and that’s why many love it. It has had a big focus on orthogonal constructs meaning that there should only be one way of doing things. The error interface is in the language specification as a pre-defined interface, but I see no real reason for it being there conceptually (I do however respect it being there, I’m sure putting it in the language spec instead of the std lib has been given considerable thought). In my mind it’s not a language construct but rather a way people choose to write code, returning a value implementing an interface. While representing errors by the error interface are commonplace, there is nothing that forces you into it and there are projects that have gone with other ways of doing this.
So why do I think try
was a bad idea? It would have added something to the language to handle a specific interface
which, in my mind, conceptually does not belong in the language specification. Meaning that you would create somewhat of
a chicken and egg problem. Go does not force you to handle errors through the error interface, which is a good thing, and
I think it would have been a mistake to further integrate the interface into the language spec. Eg. Another common pattern is to return ok
instead of an error, val, ok := myMap["key"]
and any solution should, in my mind, consider not only things
implementing the error interface but also any other value-based error handling.
Don’t get me wrong here, nowadays I don’t mind if err != nil { return err }
checks all over the place. But I did at one point and in many cases, it does obfuscate what the code does. So what is it that I would like to see in a potential solution. I would argue that we want to see a language construct that “happens” to solve the issue but is not
bound to that use case
So what would a potential solution be?
I’m not sure. But I think it would be interesting to explore a left-hand
function call with a “super return”
eg.
func something() error{
errcheck := handle(err error){
if err != nil{
return err
}
}
errcheck(err) := SomethingErrorProne()
key, errcheck(err) := SomethingElseErrorProne()
ok2Err := handle(ok bool, message string){
if !ok {
return errors.New(message)
}
}
val3, ok2Err(ok, "could not find key in map") := amap[key]
sanity := handle(min, max int){
if min > max {
return errors.New("did not return correct min max")
}
}
sanity(min, max) := findMinMax([]int{5,1,2,4,7,2})
fmt.Println(min, max)
return nil
}