One thing to mention that I have never seen mentioned or discussed
in the context of Go generics is that Go is special in that it has
first class existential types (interfaces). *Every* other language
with generics is different. They start with parametric polymorphism
(viz. universal qualifier in Curry–Howard correspondence), and
introduce existential types using rules akin to De Morgan's in
classical logic (not 100% correct in constructive logic, but true
enough as an intuition in this discussion, and true in Go, as Go
terms inhabit a finite universe of types (again different from
languages based on System Fω)).
But we *start* with existential types, we already had interfaces,
and we already had higher-rank polymorphism, even though we didn't
have type constructors! Higher-rank polymorphism is a feature Go
already has, today, even before introducing generics, it's just
expressed in a very different way compared to languages based on
System Fω, but it has to be intrinsic to any semantic formalization.
Of course, existentials are dual to universals. And just as we can
encode existentials as universals in languages without "native",
"syntactic" existentials, we can do the reverse (baring technical
nitpicks that arise from the difference between classical and
constructive logic, which as I already mentioned do not concern Go,
whose programs' terms only inhabit finite monomorphic types).
Before generics, Go could treat existentials as types different
from other types. Without type constructors, there wasn't any way
to algebraically transform an existential into anything else. Indeed,
this is (one of) the reason(s) why type assertions in Go can't be
statically-checked.
However, with generics, now we have universally-qualified type
constructors and the full power of logic that comes with it. And
this mathematical logic tells us that existentials can't be separated
from universals. Something's gotta give.
Introducing some kind of restriction on universals means breaking
the Curry–Howard isomorphism. It would also mean that universals
will be able to express less things than existentials, which seems
backwards. It also implies some lack of orthogonality somewhere.
It would be a total shame.
I don't think we need to abandon CH. Before type constructors, we
didn't need to concern ourselves with kind-safety[1] because our
types were kind-safe by construction. Interfaces are indeed separate
from concrete types, and that's a distinction of kind that has to
be preserved in the semantics.
(As an aside, the reason we could achieve so much without generics,
is exactly because they can express higher-rank polymorphism,
something that other languages without generics can't do.)
[1]
https://en.wikipedia.org/wiki/Kind_(type_theory)
--
Aram Hăvărneanu