More golang Stockholm syndrome. These arguments are always weird to me. Go has plenty of complex features that go developers don't seem to think cause too much cognitive burden (automatic gc, structural subtyping, etc.). Why is it that relatively simple and ubiquitous language features like exceptions and generics are just way too much and cause an unacceptable amount of complexity? I just don't buy it.
Turn the argument around. Are there any Java, C++, etc. developers arguing for the removal of these features from their languages? Are there people who say that even though these languages have generics you shouldn't touch them because they're bad? Do any devs with option/maybe types really want to go back to unsafe code that can fail if you forget to check for nil/null? Show me someone who hasn't drunk the go kool-aid who still makes these sorts of arguments and I might actually start listening.
I mostly agree with you, but I there could be an argument being made for C++- the generics there are done through templating, and I've seen people froth at the mouth about the complexity of C++'s template metaprogramming.
I agree though with your initial reaction to Go and generics- it seems strange to argue not to include generics due to complexity when the alternative is either type assertions from interface{} types, code generation through some sort of [preprocessor](https://github.com/cheekybits/genny), or copy-pasting code. All of these are more complex than a simple generics implementaton! Golang could even include some sort of generics less complex than Java since it wouldn't have to worry about co vs contra variant types, and Java's generics aren't that hard to work with.
I agree that template metaprogramming can get overly complex. I think it would probably be a mistake to bolt a full C++ template metaprogramming system onto go. But just a standard generics system like Java's would go a long way to improving the language.
> Are there any Java, C++, etc. developers arguing for the removal of these features from their languages?
I will. Generics in Java are a net loss in my opinion. They provide marginal static safety, no additional dynamic safety, no performance benefits, and lead to substantially more complex APIs. I'd have been happier if they were never added. I would prefer a Go-like type-assertion construct or an analogous "occurrence typing" feature.
C#, on the other hand, has a sensible generics implementation that provides meaningful utility thanks to value types. However, I am not ashamed to admit that, in the absence of value types, I'll resort to Whatever<Object> plus some casts without a second thought if I find myself doing even the slightest bit of work to satisfy the compiler.
With the exception of value types, there is virtually no difference between Java and C# in terms of parametric polymorphism. Liking one and finding the other a net loss is a bit mystifying.
Besides, the introduction of generics has added a tremendous amount of safety to what used to be typecast littered Java code pre 1.5.
How is it mystifying? You said "besides the one difference you cited, they are not different". I explicitly listed that difference as being the thing that I feel justifies the feature's existence!
> the introduction of generics has added a tremendous amount of safety
It's added marginal _static_ safety. It added no dynamic safety. Static safety is welcome, but frequently not worth the lengths people go to achieve it. Hence my comment about instantiating generic types with Object in the event that the cost outweighs the safety.
> to what used to be typecast littered Java code pre 1.5
Occurrence typing would dramatically minimize the syntactic overhead to casting. There have been research variations of Java that accomplished this: instanceof checks insert implicit casts on branches where the instanceof check is true. Similar extensions have been explored for collections to further omit the instanceof checks without requiring explicit type parameterization. For example, if a collection is only written to privately, you can infer casts from what types are inserted in to the collection. This can achieve the same safety without massive complexity increases to sub-typing, static dispatch, reflection, type signatures, etc.
> There have been research variations of Java that accomplished this: instanceof checks insert implicit casts on branches where the instanceof check is true.
By the way, Go already has this. If you have a variable, say `foo`, with a generic type, say `interface{}`, you can say
switch foo := foo.(type) {
case MyFirstConcreteType:
//foo is a MyFirstConcreteType instance here
case MySecondConcreteType:
//foo is a MySecondConcreteType instance here
default:
//foo is still the generic type here
}
> Similar extensions have been explored for collections to further omit the instanceof checks without requiring explicit type parameterization. For example, if a collection is only written to privately, you can infer casts from what types are inserted in to the collection.
This could be exactly what Go needs. Any link to some reference material on this topic?
I have a whole bunch of references related to dynamic languages and type inference, but can't find the specific one I'm thinking of on my hard drive. Sorry.
Yes, google c++ style guide (https://google.github.io/styleguide/cppguide.html) recommends to avoid exception as well as complex template metaprogramming, and if one has to be used, user visible api should avoid templates if possible.
>recommends to avoid [...] template metaprogramming,
Fyi...the Google style guide doesn't ban generics/templates. It's discouraging the "template metaprogramming" which is a technique beyond parameterized types (generics) ... e.g. using recursion in Turing Complete template programming to calculate compile-time values.
It uses generic type parameters. If you ask the author if the library would have been easier and "less complex" to write if generics language feature were removed from C++, I think we'd be certain the author would say "no."
Even without asking the author, if anyone believes the library is suffers "simplicity debt" because it uses generics, they can fork the code, rewrite it, and then prove to skeptics that the hashing library was much clearer and more maintainable without generics.
I didn't say google banned templates, you quoted me yourself, and the quote states exactly what you're saying, that template metaprogramming is recommended to be avoided.
Go isn't against generics, they are open to discussion about adding them if right approach to implement them without adding much of extra complexity would be found. Generics do add complexity, it is bearable in most cases, especially given the benefits, but it still exists.
Authors don't want to end up with something that C++ has, where you have to manually limit yourself in order for your code to not be too complex and unmaintainable, at the same moment, adding templates just to have them won't add any immediate benefit of large magnitude.
Well, your response was to grasleya and he wrote: "Why is it that relatively simple and ubiquitous language features like [...] generics are [...] an unacceptable amount of complexity?"
[...] Are there any Java, C++, etc. developers arguing for the removal of these features [...]?"
And your reply was: "Yes, google c++ style guide recommends to avoid [...] template metaprogramming,"
... and therefore, that makes it look like you're conflating "generics" with "template metaprogramming".
If your intention was to respond with a random tidbit unrelated to the proposed generics for Go, it means you're just introducing a non-sequitur and confusing readers trying to reasonably follow the context of the thread. The op (grasleya) wasn't talking about "template metaprogramming" to compare to "generics" in Go.
Google recommends avoiding exceptions for a completely different reason though: Google has a massive C++ codebase that was built when C++'s particular implementation of exceptions had huge technical issues that made dangerous to use dangerous to use. Now that Google has a bunch of code that was written as if exceptions didn't exist, they have made the decision that it will be easier for Google if they just continue to pretend that exceptions don't exist, rather than having to rework (and possibly break) a bunch of already-working code.
So that is the point. Go design doesn't allow these complexities, so there will be no confusion when one should use them and when not. This somewhat limiting, of course, but turns out it's not the end of the world, benefits may outweigh the limitations. If you really need those, C++ is always available to you.
Go allows a wide range of complex metaprogramming through reflection, struct tags, ... to say that Go is void of that kind of garbage is lying.
Go also has a bunch of weird rules regarding type conversion and assertion, its type system isn't covariant... Go has its share of problems, so much that go maintainers themselves keep on introducing type unsafe API in their own std lib. Go has a type system problem, period.
Finally all platforms supported by Go aren't first class. Windows doesn't support Go plugins or Go shared libs AFAIK for instance.
It certainly has problems, no sane person would seriously argue that it is literally and objectively perfect. Reflection is complex, unsafe library is a hack, that's exactly what is stated in the official description of both libraries and both recommended to be avoided if possible. No one tries to say opposite.
Yet, code in Go tends to be easy to read, uniform, very well suited to work on in a team. No megabytes size of code style is required, in most cases there would be no problem to understand code written by other person because of how simple the language is.
> code in Go tends to be easy to read, uniform, very well suited to work on in a team
This was not my experience working on a moderately sized Go codebase. In fact, it was one of the messiest agglomerations I've ever had the displeasure of working with (and I worked at Twitter when it was still a monorail). Everyday code that is simple in most other languages was a drawn-out mess in Go. The type system was an obstacle to be worked around. Metaprogramming (via go generate) was a joke.
That statement would be more credible if you identified that unholy mess of Go code so that we could form independent opinion.
Or if you showed a few examples of "code that is simple in most other languages was a drawn-out mess in Go" or how Go's "type system was an obstacle to be worked around" when compared to type system of other mainstream languages like C++/Java/C#.
As it stands, you're in a minority. The consensus is that Go codebases are among the most readable.
Go codebases are readable in that you can figure out what a given line is doing - but when 3 out of 4 lines of code are duplicated error handling, it's difficult to figure out what the "success case" of even relatively simple functions is.
> As it stands, you're in a minority. The consensus is that Go codebases are among the most readable.
Your statement is as unsubstantiated as his. But at least the parent doesn't feel as arrogant claiming he represents the majority. Go doesn't magically makes code more readable than any other language. It's just something some gophers like to think since they often start projects from scratch.
> The consensus is that Go codebases are among the most readable.
Among who--other gonuts/gonads/gophers? In my neck of the woods a Go application over a few thousand lines is already a yellow flag and a Go application, period, requires defending the choice over TypeScript or Java. (Difference being, of course, I'm not holding up my experiences as objective.)
I find the statements about the type system very credible. I've been programming go full time for nearly 3 years and not a single day goes by that I'm not bitten by it.
As far as whether go makes it easier or harder to make easy to manage code bases I don't know that it matters one way or another. I've written bad code in other languages and go. I will say, I've never seen a large go code base, and I suspect if I did it would suffer very much from things that people complain about with the go language.
> This somewhat limiting, of course, but turns out it's not the end of the world, benefits may outweigh the limitations.
The situation is not really benefit vs. limitations. The style guide is put as general guide. It's a proven way of writing code inside google that most people agree and think is acceptable.
It's about taking a common ground on how to use a language, rather than finding a sweet spot of trading-off. Benefit vs limitations is part of the equation, but it's put in a context that only makes sense inside Google.
Therefore it's not self-evident that the benefit vs limitation argument automatically applies to general use cases outside of Google.
Or, I do not like people use Google or any other large organization's approach to automatically prove that those rules makes sense in different settings.
I can buy using generics sparingly (especially in the form of complex template metaprogramming). But never using it at all? Can you imagine convincing the C++ community to give up Boost because its use of templates is too complex?
They also seem to admit that if they were to do it over again and start from scratch they'd use exceptions:
> Things would probably be different if we had to do it all over again from scratch.
This is when exceptions where not actually used in the code. The bloat is merely from enabling C++ compiler flags to generate rtti and exception support code.
> It seems that thinking on exceptions in low-level languages is firmly in the "no way" camp.
Except Go isn't particularly close to the metal anyway. The only way Go is low level is in the same way Java is low level: it's very limited in terms of the programmer's ability to create abstractions.
> If exceptions are so great, why Rust and Swift aren't using them?
Technically speaking, Rust does have exceptions with its unwinding feature. The panic! macro throws an exception, and catch_panic allows you to catch it. It is implemented exactly the same way as C++ exceptions, with the accompanying code bloat and performance pessimization, and you have to think about exception safety exactly the same way when writing unsafe code.
Rust has unwinding, which indeed opens a whole can of worms, but to say that it has exceptions is to miss the point. Rust has nothing anywhere near close to first-class resumable exceptions as surfaced in, say, Java. Not only is the `catch_unwind` function (it's not called `catch_panic`, btw) deliberately difficult to use (specifically to deter people from using it as a general error handling mechanism), the language deliberately defines an alternate compilation mode where unwinding does not exist and hence any attempt to "catch" it will fail. As a result, I have never once seen Rust code APIs that use `catch_unwind` as a mechanism for error handling; the purpose of that function is to prevent unwinding from crossing FFI boundaries.
> If exceptions are so great, why Rust and Swift aren't using them?
Because they made a mistake by not including them?
More seriously, you are approaching the argument the wrong way. You don't judge a feature by how popular it is but by analyzing objectively whether the presence or absence of that feature leads to higher quality code
Why would anyone try to convince Boost users to give up boost? On the other hand, designing new language, it is reasonable to consider ways of reducing code cost and complexity by avoiding such features. Large fraction of C++ developers won't understand how half of boost libraries works at all and that is type of complexity Go tries to avoid. There are tradeoffs, that is undeniable, but it is a valid approach to address this exact issue.
If think the popularity of Go as a language (despite not having these features) is an argument that they're not actually needed.
If they were really needed, then you'd have significant numbers of people trying Go and then leaving because it isn't expressive enough.
Not that that doesn't happen, but we're seeing the language gain mindshare as more people appreciate the simplicity, partly due to not having these features.
I think it's fair to argue that these features shouldn't be added to go. Not because they're bad features, but because they'd basically require a rewrite of the stdlib and, as a result, all go code.
So, generics and exceptions would improve go. It just wouldn't be go anymore.
I assumed that was what the term "Simplicity Debt" was referring to.
Heh, perhaps the right answer is "Let Go hit its natural limits and kill itself" rather than trying to get its stewards to fix it. I suppose that might teach a whole generation of programmers what happens to codebases over time when you don't have the ability to build robust abstractions (or teach the rest of us something if it actually works out). It's just scary to actually let this go (pun intended) because the more successful Go becomes, the more likely each of us is to have to work professionally in a language without e.g. parametric polymorphism.
> Are there any Java, C++, etc. developers arguing for the removal of these features from their languages?
No, but I'll bet money that there are Java, C++, etc. developers who have abandoned those languages for Go. Developers often vote with their feet before trying to change a standard.
Personally, I think checked exceptions are sometimes a headache in Java and I've worked in places where the default when encountering a checked exception was to catch and throw RuntimeException with a helpful error, which is pretty similar to Go's error/panic model.
Checked vs unchecked exceptions is kind of a separate thing from exceptions vs no exceptions. There are plenty of modern languages without checked exceptions but few with no exceptions period.
There is a valid argument about whether exceptions are the best way to signal errors or not. But most modern alternatives are some form of capturing errors directly in the type system, so that return types can carry error information (option or error types etc), and so that callers are forced to check for errors.
Oh I'm firmly in the monadic error return camp. Its just a misnomer that go doesn't have exceptions. It has both exceptions and by convention error returns. Its sort of the worst case scenario.
I will say that there is some good post compiler tooling around making sure that error handling isn't skipped but its definitely a weakness of the language.
Turn the argument around. Are there any Java, C++, etc. developers arguing for the removal of these features from their languages? Are there people who say that even though these languages have generics you shouldn't touch them because they're bad? Do any devs with option/maybe types really want to go back to unsafe code that can fail if you forget to check for nil/null? Show me someone who hasn't drunk the go kool-aid who still makes these sorts of arguments and I might actually start listening.