Ehhh, I see absolutely no evidence that the Go developers were particularly aware of theory. It really feels more like they just were used to thinking in terms of C, and built a language which is kind of like C.
Go also has some really weird stuff in it, such as named return values.
Frankly, the lack of sum types hurts the most. The language would just be a lot better with a unifying Result type in the library. And don't give me any of that "oh, they tried to keep the language simple!" stuff.
Intuitively, sum types are laughably simple. Everyone understands "It's one of these possible values, so you need to check which one it is and then handle that situation." They are more simple than enums on a conceptual level! Sum types are just not how C-programmers think about the world.
As it happens, we considered sum types quite seriously in the early days. In the end we decided that they were too similar to interface types, and that it would not help the language to have two concepts that were very similar but not quite the same. https://groups.google.com/g/golang-nuts/c/-94Fmnz9L6k/m/4BUx...
When I did research on this topic ages ago I read both of these links, and I'm very familiar with the arguments.
I also vehemently disagree with them, and I think that the way code is factually written in practice is on my side: People who propose sum types commonly refer to the Option<T> or Result<S,E> types in Rust. These are types which are almost exclusively used as return types.
Interface types are the opposite. They're used as input types, and almost never to distinguish between a concrete, finite list of alternatives. They are used to describe a contract, to ensure that an input type fulfills a minimal set of requirements, while keeping the API open enough that other people can define their own instances.
The fact that Go in fact does not use interface types for its error handling is a pretty good argument in favor of that, I'd say.
The thing is, at this point it doesn't matter, sadly. Adding sum-types to the language now would be unsatisfying. You would really need proper integration as the primary tool for error handling in the standard library (and the ecosystem), and that's unlikely to happen, even less likely than a Go 2.0.
EDIT: Just to make it clear, I think not wanting to add sum types to the language is understandable at this point. The real shame is that they were not in the language from the beginning.
Go made a deliberate decision to use multiple results rather than Option<T> or Result<S, E>. It's of course entirely reasonable to disagree with that decision, but it's not the case that the Go language designers were unaware of programming language theory or unaware of sum types. Although you suggest that you want sum types as the primary tool for error handling, Go made a different decision, not due to accident or lack of knowledge, but intentionally.
(Separately, I'm not entirely sure what you mean when you say that Go doesn't use interface types for its error handling, since the language-defined error type is in fact an interface type.)
Fwiw, I didn't mean to imply that they don't know any language theory, just that the language doesn't seem to reflect it. I don't think this itself should be a controversial statement, by the way, Go aims to be a simple language, and the last thing it needs is monads.
Frankly, I'm just the type of person who doesn't understand why it is possible to silently drop error values in Go (even accidentally), see
while the language is simultaneously very strict about eg. unused imports.
It seems like a pretty severe flaw for a language that takes pride in its explicit error handling, and a deep dive into why this flaw was acceptable to the creators would be really interesting.
For now though, instead of sum types we ended up with features such as named returns (???). I imagine some of the complexity here was about not wanting to introduce an explicit tuple-type, since a Result<S, E>-type doesn't compose with multiple returns. (I feel like there should be some workaround here. Maybe anonymous structs + some sort of struct/tuple unpacking, but I could see it getting gnarly.)
> I'm not entirely sure what you mean when you say that Go doesn't use interface types for its error handling, since the language-defined error type is in fact an interface type.
What I meant is that this specific usecase of sum-types (ie. error-unwrapping) is not something that interfaces in Go are used for. Error-handling in Go is done via multiple return values. This goes against the common claim that "sum types and interfaces are too similar/have the same uses", and should count for something, considering that explicit error handling is a big component of Go.
I'm not going to claim that Go has the ideal approach to whether an error can be ignored. In general, in Go, some errors can be ignored, and some can't. For example, fmt.Fprintf to a strings.Builder can never result a non-nil error. It's fine to ignore the error returned by fmt.Fprintf in that case. On the other hand, fmt.Fprintf to a os.File can return a meaningful error, and for some programs it's appropriate to check for that error. (Though the issue is complicated by the fact that complex programs probably use bufio which does its own error handling.)
I'm not personally concerned about examples like os.Open, where the result must be used. Sure, you can ignore the error. But a failure will show up very quickly. I'm not saying that this is not an issue at all, but I believe it's a less important one.
Part of the reason for Go's behavior is the idea that "errors are values" (https://go.dev/blog/errors-are-values). And part of it is that the simple fmt.Println("Hello, world") doesn't require any error handling. And part of it is the desire to make error handling clear and explicit in the program, even if the result is verbose.
Having worked on large golang codebases, it shows quite quickly how badly error handling is, the example you mentioned being one of them, as well as others not detected by the compiler or linter. Using multiple return values to model errors is fundamentally broken, as with other things they chose with the language, either deliberately or not.
The claim wasn't that they are unaware, the claim was that it doesn't seem to show in the design of Go.
I think Go is a perfectly fine language, and I respect the goal to stay clear of complexity, but when looking at any particular Go feature, then it's easier to explain the decision with "They are used to thinking in terms of C-idioms." than with any particular brilliance or awareness of PL-theory.
Thompson turns 80 this year. In what years does Thompson become old enough that you start to entertain the possibility that not everything he says or does is the result of having learned and understood all the possibly-relevant work, including the recent work?
I misspent a few thousand hours of my life on the 9fans mailing list long ago when Pike was very active on it, and my non-joking assessment is that ever since he finished his PhD or shortly after, Pike has probably felt he knows all he needs to learn about programming-language design except for the things he and the people in his immediate social environment invent.
Bell Labs was never good at designing programming languages. Did you know that in the Bourne shell (and possibly in all the other shells) you can have a statement of the form $foo = bar which will assign bar to the variable whose name is the value of foo? (Emacs Lisp, an old language, has the same functionality in the form of a function named "set", but most Emacs Lisp programmers know to avoid it.) Well, I found that statement in a shell script written by one the Bell Labs guys, and the shell script was not doing anything fancy like interpreting a programming language or defining a new PL feature (not that it is sane to do either of things in a Unix shell).
None of what I say is more than a wisp of a reason not to choose Golang IMHO.
Have you looked for any evidence? There is plenty.
Sum types have been discussed since before the initial open source release of the language, at least according to some of the issue threads such as https://github.com/golang/go/issues/19412
If they're laughably simple, then please contribute a proposal for how to add them to the language. You'll find no one is really fighting against the concept of sum types.
Go development began circa 2007, I do not believe sum types were common place nor do I believe the average developer was aware of them at the time. I'm not saying you're wrong, but given even Scala didn't add support until 2010 they do not seem like something sorely obvious that should have been there from the beginning. In hindsight, sure.
Go also has some really weird stuff in it, such as named return values.
Frankly, the lack of sum types hurts the most. The language would just be a lot better with a unifying Result type in the library. And don't give me any of that "oh, they tried to keep the language simple!" stuff.
Intuitively, sum types are laughably simple. Everyone understands "It's one of these possible values, so you need to check which one it is and then handle that situation." They are more simple than enums on a conceptual level! Sum types are just not how C-programmers think about the world.