> It's a bit different though in that the type parameter's bound is not the type of the variable
I don't think it's different. When you annotate a function parameter with `Foo`, you are not saying that the type must be a `Foo` object at runtime. Rather, you're saying that the parameter is constrained to a type that is compatible with `Foo` — i.e. a subtype thereof.
It's the same logic with the upper bound for a type parameter. When you say that it is bounded by `Foo`, you are not saying that it is type `Foo`, but rather that its type is constrained by `Foo` — i.e. it must be a subtype thereof.
I think Jelle is talking about the type of the thing before the colon in relation to the thing after the colon. When you say 'T: int' in a type parameter clause, that means roughly 'issubclass(T, int)'. But when you write 'a: int' in a parameter list, that means 'isinstance(a, int)'.
However, I'm not convinced this is a big enough problem to reject ':' outright.
I'm not completely opposed to using `<:` like in Scala, but I don't think most Python users will have ever seen (or heard of) Scala, so this token will seem very foreign to them. Also, I don't think it works well with constrained type parameters.
I've seen this used in papers about static types as well, without introduction, so apparently it's a standard operator in that community ("Consider types *T* and *S* s.t. *T* <: *S* ... etc.). However, it's always taken me a bit of thinking about the context to derive that '<:' means "subclass" (i.e., "extends") and not "superclass".
To inform this discussion, I've updated the draft PEP to include an "Appendix A: Survey of Type Parameter Syntax". Here's a direct link: https://github.com/erictraut/peps/blob/typeparams/pep-9999.rst#appendix-a-survey-of-type-parameter-syntax
I looked at C++, Java, C#, TypeScript, Scala, Swift, Rust, Kotlin, and Julia.
There are four patterns that emerge for specifying constraints on a type parameter:
1. Java and TypeScript use the "extends" keyword
2. C# and Rust use the "where" keyword (and place the clause at the end of the declaration — something that wouldn't work well for Python's grammar)
3. Scala and Julia use the "<:" operator
4. Swift, Rust and Kotlin use a colon
Rust uses "extends" *and* a colon? (I think I get it -- it uses the colon but you can also use a "where" clause.)
If we think that we will want to support a lower bound at some point in the future, that would argue in favor of "<:" because ">:" is the logical counterpart. However, I don't think there's a strong need for specifying a lower bound, and most languages forego this capability. For that reason, I still favor using a colon here.
The call-like syntax also easily accommodates this: just add a new keyword parameter 'lower_bound=...'.
Note that none of these languages use a function call or constructor-like syntax. Adopting that approach would make Python an outlier among all other popular languages.
Not that we care all that much about that (none of them use indentation either :-).