I think that was the case with Go from the start. Can you think of some aspect that was especially well thought-out?
It's a crapshoot what will be an interface and what will be a struct, which is one of the fundamental features of the language.
Returned error values are sometimes errors.New (e.g. io.EOF) and sometimes random fmt.Errorf strings that couldn't possibly be handled (e.g. all of tls.go basically), sometimes actual structs (e.g. PathError), and sometimes panics.
If you can't even get error handling right and consistent, what claim do you have to consistency? At least in java it's pretty much all exceptions.
The math library, as mentioned above, is pretty darn iffy.
The existence of both 'path' and 'filepath' is possibly a mistake.
The 'heap' and 'list' in collections both seem like they should be similar, but 'heap' operates on a thing that fulfills its interface and list operates directly on arbitrary objects without some interface needed.
Due to the lack of 'protected', the std library is littered with exported fields you shouldn't use (e.g. gob.CommonType and template.Template.*parse.Tree and so on).
Sure, the library is consistent in many places because the language is so small there's little else it can do but be consistent, but the fact that it falls flat in error handling and has visible warts makes me feel that it's a very broad std library, but far less consistent than, say, Rust's (where everything returns consistent Optional error types) or Java's or Smalltalks. Sure, it's more consistent than the clusterfuck that is ruby and/or python or javascript or (heaven forbid) c/c++, but by no means is being better than any of those an achievement in this category.
This is the advantage of having errors be interfaces rather than concrete values. Once you learn how to use it, it's a strength. The usage of structs that satisfy the interface (as in PathError) is clearly documented and completely predictable. If your concern is that a certain package could use a struct satisfying the interface and does not, well, one advantage of errors as interfaces is that you could propose this change without breaking the backwards compatibility guarantee.
It's really important to keep in mind that errors are adopted from error values in C, which means that their idioms and usage is inspired by that rather than by exception handling. Many new Go programmers (including me, when I started) were more familiar with exception handling in higher-level languages than error handling in idiomatic C, so it does take some getting used to.
> and sometimes panics.
Do you have any examples of the standard library using panics to signal handle-able errors (not counting cases in which the panic is recovered at the top-level in the library and never visible to the caller)?
> The math library, as mentioned above, is pretty darn iffy.
The only complaint I have had about package "math" is that all operations are defined on float64s, which can be annoying, but is far better than any alternative. That's an annoyance, but it's certainly not an inconsistency in the library.
Do you have any other examples of inconsistencies in the math package?
My biggest disappointments early on was not even having a Max/Min function for ints.
My biggest problem with the math library was, IIRC, everything only takes float64s, which makes dealing with any of the other number types more difficult than it should be (I shouldn't need to cast an int to a float and back in order to get the max of two numbers).
And some aspects of the Go SKD are horrible in practical use. Case in point, most things math related.
Also, Java has a head start of nearly 15 years on Go, and the Java community is (or used to be, at least) pretty huge.
Not that this invalidates your point.
Go is a small incredibly useful and composable set of libraries that handles a vast amount of cases with a small amount of code.
Go routines, channels and select. With very little else, it's possible to write some very useful code in a way that's concise, elegant and easy to reason about.
And Go is not quite expressive to address higher level, but common, constructs ( https://gist.github.com/kachayev/21e7fe149bc5ae0bd878 ) in a conceise and elegant way.
And if you access anything outside of channel provided stuff in Golang, you have to bring your own safety.
Maybe it's just my advancing age (for a programmer), but I find myself gravitating towards the "It's not finished when there's nothing left to add, but when there's nothing left to remove" mentality when it comes to programming languages. I find that I'm only willing to tolerate new language features when they introduce benefits that go beyond expressiveness.