6 ms·
They also made sure you can't easily tell what the code is doing due to the noise of edge case handling in 75% of the lines
by ikari_pl 3y ago
They also made sure you can't easily tell what the code is doing due to the noise of edge case handling in 75% of the lines
- cratermoon 3y agoBuggy "edge case handling" is the source of many critical failures[1]. Go makes explicit where a called function can return and also provide information for any anomalous conditions encountered. The alternative of just pretending like a return means success is wrong, and other ways to determine if the result of called function is acceptable (e.g. checking errno in C) are just as verbose and introduce other failure modes. Here's a thought experiment for you: pretend the return type is something other than 'error': result, statuscode, responseContext, anything that doesn't imply failure. Would you then suggest handling that is "noise"? ETA: "there are countless other [than if err != nil] things one can do with an error value, and application of some of those other things can make your program better, eliminating much of the boilerplate that arises if every error is checked with a rote if statement."[2] 1 https://www.eecg.utoronto.ca/~yuan/papers/failure_analysis_osdi14.pdf https://www.eecg.utoronto.ca/~yuan/papers/failure_analysis_o... 2 https://go.dev/blog/errors-are-values https://go.dev/blog/errors-are-values
- golergka 3y agoAlternative is using result or either monad and have first-class support for nomadic operations in the language so you don't have to waste three lines on every function call just to propagate the error up
- TeMPOraL 3y agoThat's just a hard-core technique for sweeping noise under the rug. It helps with this and similar cross-cutting concerts, but at a huge cost elsewhere. We are unlikely to improve on this until we finally abandon the idea of working directly on a single, plaintext codebase.
- golergka 3y agoCan you please explain, how exactly is this sweeping noise under the rug? Type system still forces you to explicitly handle the error case, one way or another.
- TeMPOraL 3y agoMonadic techniques let you hide most of the noise coming from passing around the Result type, especially in code that would only pass the error state through. You still need to handle the error case explicitly somewhere, but you avoid writing error checks and early returns everywhere else. I say it's sweeping under the rug, because you still can't exactly ignore the presence of error handling when not interested in it, and the extra complexity cost of monadic mechanisms themselves still pops up elsewhere to ruin your day.
- groestl 3y agoOr just, like, exceptions, still good enough. It's not rocket science, almost anything is better than Go's approach, and only C's is worse.
- golergka 3y agoThat's exactly what sweeping under a rug is. When you use exceptions, you throw type safety out of the window and have an implicit spooky dependency at a distance between one place in the code that throws an exception and another that catches it.
- groestl 3y agoThere's nothing magic-like with exceptions, and no spooky distance. It's what it looks like when you _really_ assume everything can fail. Go admits that with panics.
- golergka 3y agoOf course there is. If you throw a FileNotFoundError exception from function readFile(), you have to actually read documentation or it's source code to know that you have to catch this exception when you use it (in most languages except like early versions of Java). Type system doesn't check it for you. And if at some point readFile() also begins throwing InsufficientPermissionError exception, the type system, once again, doesn't tell you to fix the code that uses it. If that's not the spooky action at a distance between the place where exception is thrown and where it should be handled, I don't know what is.
- frou_dh 3y agoSpecific functions where returning a tuple of error and something else makes sense are always free to do so. Why does their existence mean that the other 95% of functions that can error need be given the wrong return type and pretend to return a tuple when they never do? (i.e. some element of the tuple will be garbage, euphemistically called a Zero Value)
- rohansingh 3y ago> Why does their existence mean that the other 95% of functions that can error need be given the wrong return type and pretend to return a tuple when they never do? They don't, do they? One really nice thing about Go is writing and calling functions that _don't_ return any error. You can be confident that they will not throw any exceptions at all.
- groestl 3y ago> writing and calling functions that _don't_ return any error You are under an illusion if you think they can't fail.
- frou_dh 3y agoI said functions that error. If it has 'error' in its return type then it's such a function, e.g. (string, error)
- ikari_pl 3y agoYou can only be confident that they won't tell you what the exception was. More likely they will panic. If you're lucky, the documentation says when.
- LispSporks22 3y ago> Go makes explicit Is kind of an understatement. If the handling code for that is duplicated as 75% of your code base, there's something wrong with the language. There's got to be some other way than all that noise.
- fasterik 3y agoExplicit error handling is a design choice, not a language defect. If you don't like it, you don't have to use the language. Many people choose to use explicit error handling even in languages that support exceptions. Knowing that every function call returns a value that is handled locally makes it a lot easier to reason about your program and debug it. Also, this 75% number sounds made up out of thin air. If your program is doing something non-trivial, it should have far more code doing work than checking errors.
- AnimalMuppet 3y agoI recall reading back in the 1970s or 80s that error checking and handling took 80% of the lines of code of production-ready software. That would be pure procedural code, not exceptions, not FP style. (And that's all I've got - one hearsay-level report from decades ago.) I have not, ever, seen any numbers for exception style or FP style. My perception is that their numbers might be lower, but I have no evidence, and I am not dogmatic about my guess.
- fasterik 3y agoI can't really imagine 80% error handling for the whole codebase unless literally all of your functions look like this: int foo(Data *data) { int error = do_some_io_request(data); if (error) log_error(error, "Request failed"); return error; } For propagating errors up the stack, the ratio is only 50%: int error = foo(data); if (error) return error; For the rest of your code, I guess it's domain specific. But most projects should have a significant amount of the codebase doing things with data in memory that can't fail.
- 3y ago
- groestl 3y ago> Buggy "edge case handling" is the source of many critical failures[1] And to fix this, we introduce 10 places per function to improperly unwind the stack, have a chance at missing an error result, and completely ignoring that fact that anything can fail anyway, even a simple addition. Instead of just writing exception safe code in the first place.
- icholy 3y agoIt's not 75% of the code-base. During the error handling proposals, the Go team analyzed a large corpus of Go, and it turns out people drastically overstate how much error handling contributes to the line count.
- jahewson 3y agoIt does make me wonder if the fact that people feel this way says something about how much cognitive effort is consumed by error handling and that it might be disproportionate.
- t-3 3y agoError handling is something you always need to think about for serious code, but often feels like unnecessary work and boilerplate for exploratory programming or simple hacks.
- cowl 3y agonot 75% of all code but 75 of all code that is doing anything with meaningful practically. the classic example of a simple copyFile func. func CopyFile(src, dst string) error { r, err := os.Open(src) if err != nil { return err } defer r.Close() w, err := os.Create(dst) if err != nil { return err } defer w.Close() if _, err := io.Copy(w, r); err != nil { return err } if err := w.Close(); err != nil { return err } }
- no_wizard 3y agocould you in theory break this into other functions or no? You could have smaller discrete functions that abstract the handling of each action a little bit and be more reusable, or is that not possible in Go
- ikari_pl 3y agolike Open, Copy...? These shorter functions need to signal their failure somehow, so calling them looks exactly like the example.
- tptacek 3y agoSystems programming is edge case handling.
- ikari_pl 3y agoAnd that's exactly why it should be expressive, self explanatory, and come at a low cognitive cost.