Comment by TheDong

5 months ago

Stringly typed errors are also idiomatic in go, if you follow the stdlib.

Like, are you doing anything with TLS? String matching: https://github.com/golang/go/issues/35234

Using the stdlib ssh stuff? String matching: https://github.com/golang/go/issues/45207 / https://github.com/golang/go/issues/39259

Want to parse an address + port? netip.ParseAddrPort only returns strings ('errors.New' errors).

http/http2 is also a minefield of half-exported errors.

The go authors say to use 'errors.Is' and 'errors.As', but the go stdlib also defines an idiom, and the idiom it defines is that somewhere around 30% of all errors should be stringly typed, including many where you may want to have specific handling for them.

> Stringly typed errors are also idiomatic in go, if you follow the stdlib.

Realistically, you can't follow the standard library, except perhaps the newest additions. Idioms emerge and evolve with use. Much of the standard library was written before Go saw much use, being largely in place before the world got to see Go for the first time. Also, thanks to the Go1 guarantee, cannot be changed now.

If the aforementioned package was written in the 2000s, then it might be fair to say that it was in line with the idioms of the time. But it appears to have been written within the last year, and thus is not aligned with idioms of its age.

That's not to say it has to be. Idioms are not requirements. The "stringly-typed" design may be justified even knowing what we know in the 2020s. And, with that, we don't need to speculate about the justifications. We can let the author speak for himself as to why the choice was made.

  • netip is one of the newer additions to the stdlib (added in ~2022), and follows the venerable stringly typed error idiom.

    I always interpreted the preference for stringly typed errors as a way to keep the Go language simpler. Good error handling is complicated and hard to read, and one of Go's values is that programs should be easy to read. As such, if you want good error handling, you should use a different language, like Java or Haskell or C++. This also helps keep people who might demand complicated things like generics away from the language, further keeping it simple.

    My understanding was that many of the go idioms are there to scare off programming language theorists, who have a tendency to unnecessarily complicate everything with type theory, and error handling also seems to mostly be in that vein.

    • > and follows the venerable stringly typed error idiom.

      Not exactly. It assumes that all error conditions within the functions provided by netip are of the same nature as it pertains to a single unit of work. In other words, there is only one type (not referring to the language's type system). Error type reuse where different failure points produce the same type of error does not violate current idioms. I cannot immediately think of any reason for why their assumption is wrong, so unless you have other ideas?

      That is not the same situation as the deferred close wrapper, though. It is assuming that closing multiple file handles is the same operation, but clearly that's not true. If you were, say, writing a copy function the error handling of the read handle failure is unlikely to be the same as the handling of the write handle failure. The former doesn't tell you much, the latter is quite actionable. The failure points are distinct, and thus of different types (again, not referring to the type system).

      The author's answer was basically that he is the only caller so if that problem arises in his code he'll simply modify the function to return idiomatic types. Which is fair for the lone wolf developer. When working alone anything goes! But it is not good API design generally speaking. It certainly wouldn't fly in something like the standard library or anywhere you have other developers.

      4 replies →