> Asked differently: If one were to design a programming language that only has basic types (primitives, structs/classes, ...) and templates to allow functions/classes to operate on any type (but not more than that; substitute template type with the actual type, compile this, nothing more), what feature will users of the language be missing and complain about?

This is a really good question. There are some languages that work as you describe: SML and some others in that family. There are generic functions and types, but the type parameters are basically just placeholders. You can't do anything with a value whose type is a type parameter, except store it and pass it to things that also take type parameters.

That gives you enough to write a nice reusable vector type. But it doesn't let you easily write a nice reusable hash table. You can, but users have to pass in an explicit hash function that accepts the key type every time they create a hash table.

It might be nice if a type itself could indicate whether it's hashable and, if so, what it's hash function is. Then, if you create a hash table with that key type, it automatically uses the hash function defined by that type.

Now you need some sort of constraints or type bounds in your generics. That's what traits in Rust and bounds in Java and C# give you. (The literature calls it "bounded quantification".) It's a big jump in complexity. But it does mean that now you can call functions/methods on arguments/receivers whose type is a type parameter, and those calls can be type checked.

Bounds are themselves types, so what kinds of types can you use in bounds? Can they be generic? If so, what kinds of type arguments are allowed? Can you use type parameters from the surrounding type?

For example, which of these are OK and which aren't (using Java-ish syntax):

class A<T extends Foo> {} // 1.
class A<T extends Bar<Foo>> {} // 2.
class B<T extends B<Foo>> {} // 3.
class B<T extends Bar<T>> {} // 4.
class B<T extends B<T>> {} // 5.

Any kind of bounded quantification will give you 1-3. What about 4 and 5? This is called "F-bounded quantification". Why would you want such a thing?

Collections with fast look-up are important, which is why we extended our generics to enable us to write nice reusable hash tables. But some data types aren't easily hashed but can be easily ordered. A sorted collection is faster than an unsorted one.

How would we write a generic sorted collection? We could require you to always explicitly pass in an ordering function for any given element type, but it would be nice if the element type itself could supply is order function.

You could define a "Comparable" interface that a type can implement to support comparing an instance against another object of some type, like:

interface Comparable<T> {
int compareTo(T other);
}

And then implement it on your type, like:

class Color implements Comparable<Color> {
int r, g, b;

int compareTo(Color other) => ...
}

In our sorted collection, elements all have the same type, so the bound that we need looks like:

class SortedCollection<T extends Comparable<T>> { ... }

Notice that we have "T" inside the bound. That's F-bounded quantification.

Using type parameters inside a bound isn't the only place recursive types like this show up. Let's say you wanted to make a generic type comparable. You'd do something like:

class Pair<T> implements Comparable<Pair<T>> {
T a, b;

int compareTo(Pair<T> other) => ...
}

Now here, the implements clause is using not just the type parameter of the enclosing type, but the entire type.

We had a couple of fairly modest goals:

* Be able to create reusable hash tables where the hash function is inferred from the key type.

* Be able to create reusable sorted collections where the comparison function is inferred from the element type.

And in order to get there, we needed generics, bounds, and even F-bounded quantification.

Adding even a little more usefulness to our collection types will quickly have us reaching for variance annotations, associated types, and even more exotic stuff.