The problem you're describing already exists, there's no preventing it or getting rid of it, and generics are tame by comparison - at least they semantically mean exactly what the developer intended them to mean, don't come with non-obvious performance downsides, and don't clobber existing concepts like error handling.
As for optionals vs separate bools, love 'em or don't, but yes, there is a downside, which is in my view possibly Go's only regression from C: no longer being able to stick the function call in the if-statement directly without additional fanfare.
Now as for the rest. Generics don’t automatically make code slower. In fact, in some places, the practical effect should make code faster; for example, the sort package currently defeats escape analysis even though it shouldn’t, and a monomorphized version should not suffer that consequence. Also, obviously, the fact that it removes runtime dispatch will also lower overhead. I understand that.
However, generics DO have some consequences:
- Every part of the toolchain—the compiler, linters, etc.—needs to understand generics and handle them properly. This is a non-zero cost; generics-heavy code will have measurably slower compile times. Go compilation times are fast, and that’s a feature.
- Generics improve the ergonomics of certain patterns that are absolutely slower. It is totally possible for someone to write a “map” function today for every type they could ever want, but it’s not really practical, and since it forces you to just write the loop out anyway, it really begs the question why you would bother in most cases. With generics, it should be relatively easy, but without aggressive optimization, it will be less efficient than the simple For loop. The map call and closure would need to be inlined for the optimizer to be able to get back to the same point. Having to do this, again, will slow down compilation more.
Honestly, you’re right; it’s far from the end of the world. However, I am pessimistic because to me, it’s unclear the costs of generics will be outweighed by the benefits of generics, but once the cat is out of the bag, it’ll be hard to ever go back on it.
Personally, I believe there are other improvements that would’ve been more valuable to consider first, like, personally, sum types and basic pattern matching. Sum types feel like something that, while it would have costs, has a potential to benefit a wide variety of programs and make certain code less error prone in a way that I don’t expect generics to.
(I realize I’m replying ridiculous fast here, but I just happen to click over to Comments on the first minute. Dumb luck.)
Of course, I could just use Rust, but it’s much harder to make idiomatic Rust libraries than it is idiomatic Go libraries, and I think that fact stunts the ecosystem a bit.
Go may encourage you to use a map to an empty struct for a set, or a weird if statement syntax that contains a full statement instead of a more elegant looking match or for expression, but I find its little quirks to be manageable and without a terrible amount of consequence. Generics will probably bring some good, like the end of needing weird slice tricks, but I’m still concerned it won’t be overall worth it.
Instead, what is likely to happen is, a lot of problems that generics can be applied to, they more often will be, simply because the option is on the table, regardless of whether it really improves anything. Go taught me to stop worrying and trust the For loop, and now we’re probably going to have dozens of utility libraries with Map and Reduce routines that people will use for extremely simple loops that don’t need them, probably resulting in slower code that takes longer to compile that needs a slower and more complicated optimizer to make up for it. In my opinion, the antithesis of what makes Go great.
Not saying generics is all bad, or that reduce is bad, or anything like that. But once a programming language has a new way to do something, you can be damn well sure people will use it, often even to their own disadvantage. Go doesn’t have pattern matching control flow, so the best degree of soundness you can pull out of an Optional type is ugly visitor-pattern type interaction. And yet, I’m worried routines will superfluously return Optional types anyways, just because you can, even though it’s not really any better than just returning a type and a boolean.
And yes. Actual code that actually needs generics for real will benefit, undeniably. Data structure and generic algorithm implementations will benefit greatly. The ugly sort package can finally be cleaned up. That’s great. But, most Go programs simply aren’t suffering from a lack of basic generics. Operative words: most, and basic generics. Many programs simply don’t need any data structures other than a basic growable vector type and a basic hashtable, and Go has both and they are built-in and work generically. Yet the more complex problems that C++ templates and Rust generics solve will probably not be aided at all by Go generics, which are simply much more limited in scope.
And despite that, this is quite an increase in compiler complexity. I know many are thinking “how hard could it be? Tons of languages do it today”—but Go isn’t really like all of the other languages. The simpler compiler is a huge benefit to the compiler and the whole ecosystem. Arguably, Go’s simplicity enabled them to experiment with things that wouldn’t be very easy to do in other languages, with regards to its GC and threading model. They’ve got it figured out of course, but it really isn’t a simple walk in the park. Any single aspect has a lot going on:
https://github.com/golang/proposal/blob/master/design/generi...
My hope is that the relatively mature ecosystem of Go will somewhat encourage people to keep their code simple and use generics sparingly, but with such a major shift in the language it’s hard to imagine what is “idiomatic” won’t change over time. I feel Go’s brutalist simplicity is its strong point and that it will just be an inferior option if you try to write code in it like C++ or Rust.
Of course… we’ll see.