3 ms·
Here is the same "actual CopyFile function" using a fictional Go Result type and a little sugar, which is what Go should be doing: func CopyFile(src, dst s
by topspin 4y ago
Here is the same "actual CopyFile function" using a fictional Go Result type and a little sugar, which is what Go should be doing:
func CopyFile(src, dst string) Result<> {
r := os.Open(src)?
defer r.Close()?
w := os.Create(dst)?
defer w.Close()?
io.Copy(w, r)?
}
Yes I know this isn't likely to be the precise syntax; it's pseudocode for illustration only. Result<> would imply an "error", a miniscule adaptation for a working programmer.
Every case of err != nil has to be "thought about" or it won't compile. Nothing is ignored. Explicitness hasn't been reduced one meaningful iota. All the error handling branch noise is gone.
Retrofitting this onto Go is obviously not trivial. Obviously this won't work for all conceivable Go constructs. However, it will work almost all the time and immediately solve one of the worst aspects of Go.
Some of the claims in this thread are really frustrating. First, just because some people have a problem with the incessant "if (err != nil) { return err }" plaguing all Go code doesn't mean they want to "just ignore errors." They want a better solution. Second, the frequency of "if (err != nil) { return err }" does not indicate some failure on the part of the developer that they need to "fix." There is no fix in the prevailing Go language other than obfuscating code behind ever greater abstractions, the exact opposite of the intent of the Go programming language.
- deleted 4y ago[deleted]
- tgsovlerkhgsel 4y agoWhat's the benefit of the result type here, instead of keeping the "error" in the last spot? This would require refactoring everything, leaving you with "old style" and "new style" APIs. Couldn't "?" just mean "strip the error", i.e. act as if the function only returns the value(s) it returns, and if the "?" "triggers", it would just return default/empty values for all parameters + the error in the error field. This also allows seamlessly mixing the abbreviated version and the full current approach if you need it for some reason. I honestly also don't see how retrofitting it wouldn't be trivial. One could probably even write a preprocessor for that. The most common argument I've heard is that it will lead to less explicit error messages because people will just pass through the original error instead of f, err := os.Open(savegamepath) if (err != nil) { return nil, fmt.Errorf("Couldn't load savegame: %s", err) } so providing some convenient way to add this information would be great. Especially if it could be added once per function, and the whole system would preserve the stack trace like exceptions in any sane language do. Just adding the stack trace alone will make the additional context a lot less important. I'd rather have human-written context and the stacktrace, but if I can have only one, I'd rather take the stack trace, especially since humans will be lazy and often omit the context.
- topspin 4y ago> What's the benefit of the result type here Only to be consistent with the more general case: func tryToGetSomething() Result<Something> {...} Or perhaps you mean why not: func CopyFile() Result<error> {...} Yeah, sure. I don't care. I'm just tired of typing and wading through if (err != nil) { return err } all day long. Go has generics, so why not magic the e r r o r into place and save the species a few billion keystrokes? > I honestly also don't see how retrofitting it wouldn't be trivial. I'm not the first to propose this. Others have and the naysayers are legion, citing one corner case after another. I argue that the corner cases aren't worth solving, perfect being the enemy of good. The complier is free to bail on whatever outlier gymnastics someone might try. > One could probably even write a preprocessor for that. I (and many others) abhor the concept of a precompiler/macrowhatever for Go (although some have resorted to them for reasons.) Go is supposed to be a fast and self contained; embiggening the tool chain with more stuff is the opposite of the intent of Go. > it will lead to less explicit error messages f := os.Open(savegamepath) ?("Couldn't load savegame: %s", err) f := os.Open(savegamepath) ?{decorateAndReturnError(err) {...}} Or similar perhaps. Error handling is common. It's worth bending the rules a bit for a gain in concision. A little pragmatism would go a long way here.