This is such a big deal, I don't understand how it's defensible. Code is read dozens of times more often than it's written. When code looks like:
if val1, err := thingImActuallyTryingToDo1(); err != nil {
return nil, fmt.Errorf("thing failed: %v", err)
}
if val2, err := thingImActuallyTryingToDo2(val1); err != nil {
return nil, fmt.Errorf("thing failed differntly: %v", err)
}
if val3, err := thingImActuallyTryingToDo3(val3); err != nil {
return nil, fmt.Errorf("thing failed again: %v", err)
}
it's impossible to tell at a glance how the function is actually working. You have to parse out 90% garbage just to get to the details of what's really happening.Worse is that it's never this "clean" and repeatable in practice. It becomes a bunch of subtly-different variants on that theme (append if the function wasn't an error, or do something different if it errored, etc.) that the actual logic of your function is obscured in a maze of error-handling.
Code doesn't become easier to read/understand by just tucking away 90% of it (the hard/interesting parts at that, mind you).
If 90% of your code really is unique, specific action to error conditions, then by all means, this topic isn't for you.
But if 90% of your code is "stop processing in this function, log the error, and pass it up the stack", then why not abstract that away and reduce the noise? (i.e. something like exceptions or monads or arrows or ...)
After a while, you just learn to ignore the `if err != nil` and get on with your life. I mean, sure, it would still be nicer to have an abbreviation, but it's not like Go code is 90% `if err != nil` (as critics always try to frame), and when I read Go code, I'm most definitely not spending most (or any) of my time stumbling over them.
And most of your code winds up doing error checks because if any call in the stack under a particular function produces an error, it usually winds up having to bubble the entire way up the stack.
I understand that. I was pointing out this is less noise than try-catch-finally (assuming the programmer is comparing Go to C++/Java/C#). I don't deny some functional langs have more elegant handling in this respect.
If you want some data, "if err != nil return err" contains the four most frequently used words in Go code. See https://anvaka.github.io/common-words/#?lang=go . For comparion, try/catch are only 34th and 35th for Java, and even lower for C# and C++.
Every call that can fail, which isn't quite every function call.
These things are typically done by exceptions and monads; the "zig" language has some interesting syntax around this kind of thing as well.
But just multiple return values isn't enough for me - too much "if error return error" noise for my taste.