Description
There are two issues around type variables:
- What function to call a type variable
- How to specify constraints (and what do the constraints mean)
Currently, mymy uses
T = typevar('T', values=[t1, t2, t3])
while the PEP currently proposes
T = Var('T', t1, t2, t3)
In any case it should be noted in the PEP that this is not the same as
T = Union[t1, t2, t3]
The reason is quite subtle, and the best example is currently the predefined type variable
AnyStr = Var('AnyStr', bytes, str)
Consider a polymorphic function that does something to filenames and works on both bytes and str, e.g.
def extension(file: AnyStr) ->AnyStr:
return file.rsplit(b'.' if isinstance(file, bytes) else '.', 1)[1]
We really need AnyStr to be a type variable here, because we want to express that if the argument is a bytes, so is the return value, and if the argument is a str, the return value is too.
But that's not all! Such a type variable is constrained to exactly the given types. Consider the case where the argument is an instance of a user-defined subclass of bytes. We don't want the declaration to mean that the return value is then also an instance of that same subclass -- we want it to mean that the return value is a bytes (not a str).
I believe this makes the use of such a type variable equivalent to a collection of overloaded functions; in the above example
def extension(file: bytes) -> bytes:
...
def extension(file: str) -> str:
...
Open issues:
- What should the rules be if the type variable is unconstrained?
- Other languages often have a syntax for specifying a different type of constraint on type variables, e.g. the presence of a certain method; and then the implied type usually does vary with the actually specified type (I think). Do we need a way to do this?
- Should we name this Var(), typevar(), or something else? (Var() is a PEP 8 violation.)
- Should the constraints be additional positional arguments, or a keyword argument? If the latter, what should it be named?