> Only thing that bothered me was hearing an interview with the Go devs where one of the key devs sounded like Generics would never make its way into Go and it put me off the way he seemed so adamantly against such a feature
Everything about the development trajectory of Go so far indicates that What Is Right And Good at any given moment is largely determined by whatever makes building the compiler easier and not what makes the lives of external developers easier, until the external developers get loud enough about how the language is failing to learn from the mistakes of the past that the internal team relents with an "ok, ok, you win, our bad".
And if I never see another apologist refrain of "You don't need <x>. Just use this code generator to flood your repo with thousands of lines of project-specific-for-no-good-reason boilerplate" again it will be too soon.
I think most Go devs have been pretty happy with Go’s trajectory. I think it’s mostly the people who aren’t using Go and are unlikely to start using Go no matter its feature set who are the ones who are mostly ignored. It has also been a really good thing that Go “doesn’t learn from the ‘mistakes’ of the past” or else it would have exceptions and monads and lifetimes and inheritance and a Haskell-like syntax.
Go isn’t perfect, but it’s wildly more productive in my experience than any other language, and that matters a lot more to me than being able to be maximally expressive or abstract.
I've coded golang for four years. Web services. I hated it at the start and I hate it even more now. The generics are miserable to use and are very limited in what they can do, the standard library is awful (database/sql in particular is offensively bad, but you have to use it because all the third party tools rely on its interfaces), there's footguns and dangerous syntax subtleties everywhere, and the idea of the zero values for uninitialized struct fields is to me an even worse mistake than the concept of null. At least if you make a mistake with null the program crashes, which is obvious, instead of just silently doing the wrong thing, which is not. The language claims to be simple; it's not, it's just pointlessly restrictive in weird ways wherever it happens to offend Rob Pike's personal sense of aesthetics. At least it compiles quickly, I guess???
I wish we could use some boring language that works, like C# or Kotlin.
With NRTs this is a warning however for class-typed fields. Also `required` forces you to assign a value to it. Honestly I always thought struct init semantics in C# were slightly archaic and F# does it better by being strict in a way similar to Rust, so touching Go afterwards is a really big step back.
(i.e. you can author default constructor and field initializers for structs, in C#, the main "hole" remains the use of 'default(MyStruct)', but at least it is explicit and you get what you ask for)
The real problem is downcasting (and virtual dispatch as a form of implicit downcast during dispatch). Consider:
new Derived();
class Base {
public Base() { Foo(); }
public virtual void Foo() {}
}
class Derived: Base {
public string s; // non-null!
public Derived() { s = "abc"; }
public override void Foo() { WriteLine(s == null); }
}
This will compile without warnings even with #nullable enable, and will print True at runtime. Note that this could also be rewritten without virtual methods by doing a downcast or pattern match:
void Foo(Base b) { WriteLine(((Derived)b).s == null); }
C++ solves this problem by making all virtual calls dispatch to Base until Base() completes (which involves swapping vtable pointers at runtime as needed), and then doing the same in reverse for destructors; it also makes the type of object Base for all other purposes like dynamic_cast. That is, in effect, in C++ the actual type of the object changes as it is constructed or destructed. Which is great from a theoretical point of view, but very counter-intuitive unless you understand the problem it's trying to solve.
That aside, with respect to default(T), the other problem is arrays. If T is non-nullable and you ask for a new T[1], what should the value of the element be? C# lies and allows it to be null even for non-nullable reference types, which means that (new string[1])[0].ToString() is an NRE with no compiler diagnostic even with #nullable enabled. In C++, you actually cannot do this at all if T doesn't have a default constructor, but this then means that the language needs stuff like in-place new and explicit destructor calls to actually make a generic dynamic array possible to implement.
I don't think C++ is good for comparison here - there's a huge elephant in the room of how much worse the overall experience of using C++ is both in terms of memory safety and convenience.
It's not as common of a problem in C# to just initialize the array to the elements you need. Most arrays in regular code you will see will have their elements initialized or materialized from LINQ anyway.
I agree that NRTs are "leaky" (and wish they did it better) but I do not run into the issues you do it seems and they are a massive productivity improvement, their static-analysis-based nature also helps with not dealing with some of the ceremony caused by e.g. Option<T> in Rust.
On the snippet above - it will print true only when called inside the constructor, when the field has not been yet assigned to. It will print false otherwise. I do not see this as an issue, although it is indeed subtle.
C++ is the only similar language I know of that actually tries to tackle this problem.
And to be clear, I'm not saying that nullability checking is a bad idea! On the contrary, it's great. It's especially great when the type system doesn't lie to you (i.e. if something being not nullable actually means that it can never be null), but even partial enforcement is better than nothing.
However, the OP to whom I responded specifically complained about "idea of the zero values for uninitialized struct fields" in Go, and then in the same breath mentioned "language that works, like C#" - which is rather ironic given that C# does, in fact have this exact thing, and I tried to explain why it needs it.
Both (for example) Russ Cox and Ian Lance Taylor have contributed some very notably bad ideas to the project, yes. Does rephrasing it to "Rob Pike and people who think like him" make you happier?
"bad" ideas? Really? Are you so sure? Are your ideas really any better? What language have you made that has seen more success than Go? Clearly if they did such a bad job it would be easy for you to fix it all up and tell everyone about it.
Go is basically a reinvention of a 30-year-old wheel. Why would someone who dislikes it for that reason want to reinvent the wheel themselves? There are already many better languages out there.
In any case, designing and even implementing a PL better than Go is not a particularly hard thing to do. Making it popular in this day and age, on the other hand, generally requires a large corporation backing you.
I'm kind of glad for that. There are certainly some ideas I wish Go adopted--I could do without zero types and I wish there were enum types. But I'm glad Go eschews monads and higher kinded types and functional syntax and inheritance and exceptions and classes. I'm also glad Go introduced ecosystem-wide style checking, effortless static compilation by default, ~zero config build tooling, test/profiling/etc tooling built into the standard toolchain, low latency garbage collection, etc etc etc. I'm glad it does not require dedicated package or documentation publishing steps.
> There are already many better languages out there.
Not really. Even if you're just looking for a reasonably productive mainstream language with effortless native, static compilation Go is likely the only language that fits the bill.
How come a reinvention of a 30 year old wheel is more successful and popular than pretty much every language that attempted to evolve the wheel during that time?
"It's because people who like Go are stupid and companies need stupid people to write stupid code" may have made you feel smug and secure during the last 15 years, but what about the next 15? Or the 15 after that? Are you still going to be complaining about the stupid 70 year old wheel made by that dumb guy Pike that you hate so much?
Yes, and all are equally offensive to the average Go hater, all of them have NULL, and all of them have terrible error handling. It's not "argument from popularity", it's "argument from success". Some things succeed. Other things don't. It would be worthwhile for you to investigate why things succeed despite all the hangups you have with their flaws instead of lamenting them because the things you like aren't succeeding.
It's pretty clear at this point that your "average Go hater" is largely a strawman. There are many reasons to dislike Go, and different people have various combinations of pet peeves that don't neatly translate to this mental image of, "they must also hate C# and C++ and Java then!".
Argument from success disregards the simple fact that, in this day and age, no language succeeds without massive corporate backing - and, conversely, a large corporation can throw money at a language to prop it up in a situation where it would struggle to get market share otherwise. So, no, the fact that Go is as popular as it is, is not particularly interesting. If it actually overtook Java - its most direct spiritual competitor - then yeah, I'd consider that a more serious data point.
Same. People go around listing relatively controversial improvements but I think we can all agree on this one. It is the one that'd make my life easier the most when it comes to day to day stuff.
It should be trivial for you to google for go code generation and the cases and reasons for it. That you've personally never done it speaks only to the bounds of the work you've done.
I’m playing devil’s advocate. Again, I worked with Go in a production setting for 3+ years and didn’t need to do any code generation. Maybe my use case was simple… or maybe it’s unnecessary.
Everything about the development trajectory of Go so far indicates that What Is Right And Good at any given moment is largely determined by whatever makes building the compiler easier and not what makes the lives of external developers easier, until the external developers get loud enough about how the language is failing to learn from the mistakes of the past that the internal team relents with an "ok, ok, you win, our bad".
And if I never see another apologist refrain of "You don't need <x>. Just use this code generator to flood your repo with thousands of lines of project-specific-for-no-good-reason boilerplate" again it will be too soon.