Earlier quoted context omitted.
This entire discussion is about the type system and how it interacts with the language. Go does not have sum types, so the producer cannot signal to the consumer what it can produce and what the consumer has to handle. The consumer in Go must pessimistically assume the all combinations of return values are possible, where in a language with a decent type system the possible cases can be enumerated and handled appropr…
> In Go, you are always forced by the language to return a value of type "A or B, including both or neither." The consumer isn't any more in control, it just has to guess whether "both or neither" are possible return types. it does not have to guess convention dictates either one or the other is convention enough? is it roughly the same as compiler-enforced rules? you're free to say "no" but it is not like a guarante…
Gopher Wrangling: Effective error handling in Go
251–260 of 310 posts
Re: Gopher Wrangling: Effective error handling in Go
#252Earlier quoted context omitted.
Error handling is a difficult topic. Generally, the more you can catch in the compiler, the less you have to write runtime checks and the obligatory unit tests that everyone likes to forget. So if you are on the lookout for a language, I'd look for something that has explicit nullable/non-nullable types, as well as strict and static typing. However, I wouldn't pick a language purely based on its error handling capabi…
> Over 10k lines of code it becomes really hard to keep things straight. However, that's more due to its very limited scoping abilities. Could you please elaborate more on this?
In larger projects, especially with lots of contributors, I found that you'll want to practice some level of defensive programming because code review can only catch so many errors. In other words, you'll want to make sure that parts of your program only talk to each other via "sanctioned" APIs. Go makes this difficult because anything in the same folder can access data in structures defined in the same folder.
For example, the Kubernets backend for ContainerSSH (https://github.com/ContainerSSH/libcontainerssh/tree/main/in...) has two parts, one dealing with the integration with the rest of ContainerSSH, while the other deals with Kubernetes. In order to provide some level of separation, we added an interface in the middle to document the package-internal API somewhat: https://github.com/ContainerSSH/libcontainerssh/blob/main/in... However, this is suboptimal and leads to unnecessary boilerplate code.
You could, of course, forego the interface, but that would mean that code reviewers would have to make sure nobody is taking the easy route and messing with the internal state of a struct that they have no business messing with. In other words, you'd want some level of encapsulation which is not easily doable in Go.
In other languages you have much more granular visibility options, either in an OOP way (private, protected, public, package-private, friend classes, etc), or by providing things like header files that describe an interface between parts of the application.
Re: Gopher Wrangling: Effective error handling in Go
#253Earlier quoted context omitted.
You basically end up reinventing stack traces, with all the possible ways context can be missed. This summarizes the language quite well.
stack traces are tools for developers, error messages are tools for users users should not see stack traces
Re: Gopher Wrangling: Effective error handling in Go
#254Earlier quoted context omitted.
You have conveniently left out types from go’s list.. with those removed it is hardly longer, and as has been shown (case-sensitive identifiers), not all language complexity lives within keywords.
the parent's point was to compare the set of reserved keywords in the two languages types do not enter the discussion
Re: Gopher Wrangling: Effective error handling in Go
#255I know there's lots of complaint about error handling in Go, but I always liked it. I find it straightforward, intuitive, and it forces you to be absolutely explicit if you _really_ want to ignore something.
Re: Gopher Wrangling: Effective error handling in Go
#256Re: Gopher Wrangling: Effective error handling in Go
#257Earlier quoted context omitted.
the parent's point was to compare the set of reserved keywords in the two languages types do not enter the discussion
They do if 1/3 of java’s keywords are there due to types.
like, it's not as if the go type `float64` is also a go keyword
but i guess the java type `byte` is a java keyword? according to https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_k...
Re: Gopher Wrangling: Effective error handling in Go
#258Earlier quoted context omitted.
I agree that there is room for improvement, but I don’t mind Go’s errors that much. Using a linter to make sure errors are checked doesn’t seem like a major problem (you have to run a linter anyway, so what’s the harm?); most Go developers reflexively check errors for everything besides fmt.Println anyway. It would be better to put this in the compiler I suppose, but not a major deal. Also worth noting that Rust does…
Unfortunately, fmt.Errorf makes errors.Is/As useless. In fact, errors.Is is mostly useless in general, since very few Go libraries have any error types at all. You're usually stuck with parsing error messages if you actually want to handle errors programmatically, even for much of the standard library.
Re: Gopher Wrangling: Effective error handling in Go
#259Earlier quoted context omitted.
Result is better because it actually encodes the correct situation. You either get a file or an error. Not neither, not both. Go's encodes instead "you may or may not have a file" and "you may or may not have an error". Not the same thing, and extremely rarely what you want, IME. Other languages also do a better job of helping you verify that you actually handled both cases too. By the way I wouldn't say we need Mona…
I love Rust but honestly the Go way works fine, even if it isn’t as strictly correct as Rust. I don’t think I’ve ever seen a case where a Go function returned neither a value nor an error, or both a value and an error. What I like better about Rust, and what I think most people are actually complaining about with Go, is that syntactic sugar like the ? operator and functions like unwrap(). It’s a lot more concise and…
That's kind of the point. The type system should be powerful enough to disallow those cases then.
In practice, I've seen both, always accidentally. I've also (more commonly) seen a lot of confusion and annoyance around:
Okay, so this has to return a pointer for the error case, should the caller check that? If not, how do we square that with checking for nil pointers being generally a pretty good rule? If we do check, our unit test coverage has a blemish for every call since nothing can hit that. If we skip it being a pointer, then it's a zombie object.
It's just a lot of cognitive load and bikeshedding around an issue that shouldn't exist.
Re: Gopher Wrangling: Effective error handling in Go
#260Earlier quoted context omitted.
There is no going around abstraction, that’s a necessary part of any non-trivial program as that’s the only method we have to control complexity. Your struct is also an abstraction, you could have defined another one, use it differently, etc. Many of the design patterns are useless bullshit, that is long superseded by a language feature, so that point doesn’t stand imo. Go also has public/protected, it is just case-s…
It is easier to know that lowercase is package-specific, uppercase is exported, than knowing which field is private/public by default. Go reserved keywords: break, default, func, interface, select, case, defer, go, map, struct, chan, else, goto, package, switch, const, fallthrough, if, range, type, continue, for, import, return, var Java reserved keywords: abstract, continue, for, new, switch, assert, default, goto*,…
const true = false
in golang
The number of reserved keywords is not a bad thing. For example, I constantly missed `final` when I worked in golang. Just because a keyword doesn't exist does not make its usecase disappear. Same with other features like `enum` (extremely useful) and visibility rules. golang only has package private and public, not nearly as granular as one needs in practice, not to mention generating large CL's when changing the visibility rules of a function, as opposed to simply having a 1 line change. Sure you can ignore those use cases, but it doesn't mean that their usefulness disappears.