Generics aren't magic, and they aren't Turing complete, like C++ templates. They're just a way to avoid copy-pasting code.
Having `Set<Foo>` and `Set<Bar>` is far more readable than `class FooSet` and `class BarSet`, where the code is exactly the same aside from a search-and-replace.
You also run into issues where someone finds a bug in `FooSet`, but doesn't know `BarSet` exists, and forgets to patch both. Now, you have two divergent copy-paste classes.
Generics solve a bunch of real-world problems, in a very simple manner.
Sorry, when I said new data structures, I meant containers like maps and list, which I very rarely get to create day to day.
I can see it for your transformations, but I have seldom seen cases where generics would really help (usually we're talking about comparing complex structure types that will need custom code anyway).
"Theorems for free"! By which I mean, when I'm coding something and it could be universally quantified on the type, then it's better to do so. That way, it's impossible to phrase certain errors. For example, a function `Set<a> -> Set<a>` must produce a subset of the input set; that's guaranteed by the types. I can't accidentally include the value 0 in my set, because 0 isn't of that generic type. The generic forces you to think in terms of the structure you're manipulating, rather than in terms of its contents.
You can't use interfaces to prevent inserting values of the wrong types into a set. When you view the purpose of types as preventing bugs, that seems like a giant missing feature.
“Can you give a real world example that couldn't be solved with interfaces?”
Of course not. Go is Turing complete, and generics do not make it Turing-completer (whatever that may mean)
The question isn’t about what can or cannot be done, but about expressiveness, ease of understanding for humans versus language size (even if you have plenty of disk and RAM, That correlates with buggyness of the compiler) and compilation speed.
I think you're looking at it wrong. You can absolutely solve it with interfaces, the problem is those interface methods are identical, so it's duplicative.
> I think you're looking at it wrong. You can absolutely solve it with interfaces
There are lots of generics use case you either can't solve at all with interfaces, or you have to contort every which way and usually lose something in the process (type-safety, performances, readability, …).
> Can you give a real world example that couldn't be solved with interfaces?
There are none, you can always work around them, with the only downside is that you'll move some potential compile-time errors to runtime errors.
But that's the wrong conversation to be having; we could also say "why do we need floats? Everything can be solved with ints too", which is true, but also a lot of work and a poor trade-off. Similar arguments exist for many language features.
So it's a question of trade-offs: how much time will this save people? Will it reduce faults? And what are the costs of adding this? And how do they balance?
Having `Set<Foo>` and `Set<Bar>` is far more readable than `class FooSet` and `class BarSet`, where the code is exactly the same aside from a search-and-replace.
You also run into issues where someone finds a bug in `FooSet`, but doesn't know `BarSet` exists, and forgets to patch both. Now, you have two divergent copy-paste classes.
Generics solve a bunch of real-world problems, in a very simple manner.