-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Allow type aliases such X = List[T] if T is a type variable #606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is not included in PEP 484, so closing this. |
Whoa, maybe this should be added to PEP 484? I still am in favor of this. |
In particular, PEP 484 (https://github.com/ambv/typehinting/blob/master/pep-0484.txt) contains this example:
|
What about something like this:
|
The implementation in typing.py requires that you write
and then X would have 3 parameters (U, T, V). That felt too complicated, so I retreated on the rule that you can't change the number of parameters at all, and substitutions must be either completely concrete or single type variables, and the resulting type still has the original number of parameters. Maybe we need to find a middle ground, where your example is allowed and does in fact reduce the number of parameters. But you could still have only substitutions that are either completely concrete or a single type variable. An edge case would be |
Just stumbled upon this, the confusing thing is that the error is reported not at the line of the type alias, but at the line at which the TypeVar (
|
With mypy 0.4.4 I'm getting the error at the line of the type alias: from typing import TypeVar, Tuple
T = TypeVar('T')
U = TypeVar('U')
X = Tuple[T, U]
|
+1 for this I'm developing a dependency injection library where types are first-class citizens (users can request instance provided a type), that's why I need either generic aliases or ability to define generic types at runtime. |
I think we shouldn't allow this, but should instead allow definition of parameterized type aliases. Having the type variable be explicit makes a big difference in understandability and power. # in module vector
T = TypeVar('T')
Vec = List[Tuple[T, T]] Let's say we want to define # in module __main__
from vector import Vec, T
def append(vec: Vec, elem: T) -> Vec: ...
U = TypeVar('U')
def map(vec: Vec, f: Callable[[T], U]) -> ???: ... Note that we have to use Here's how it would look with parameterized type aliases: # in module __main__ with parameterized type aliases
from vector import Vec
T = TypeVar('T')
U = TypeVar('U')
def append(vec: Vec[T], elem: T) -> Vec[T]: ...
def map(vec: Vec[T], f: Callable[[T], U]) -> Vec[U]: ... With parameterized type aliases, all the relationships between parameters have been made explicit, and you can express concepts like Another problem with the proposal as it stands is that type variables only really make sense in context. For example, you can't have a top-level variable with type To sum up, parameterized type aliases would be both more powerful and more understandable than non-parameterized type aliases, and I think we should implement them instead. |
Just note that this would require a corresponding PEP change. TBH I'm not
excited about parameterizable type aliases, maybe we should just disallow
them in the PEP (as mypy already disallows them).
|
I agree with @ddfisher that the original The problem with
though that exact syntax isn't possible without a previous definition of |
I also agree that the original formulation is not a good idea. Making generic types indexable if they have type variables could likely be made to work with some heuristics and I believe that it would be at least be marginally useful, but I don't have a strong opinion. I'd suggest finding several real-world use cases where this could be used before making a decision so that we can validate whether this actually looks worth implementing. If the benefit is minor, it might not justify the extra rules and implementation complexity. If we want to support it, we'd perhaps want to make For cases like |
Then we need at least an issue in the typing repo reminding us that we
probably have to change the PEP.
|
@rwbarton Maybe I misunderstand you, but it looks like you a talking about an outdated version of >>> Vector = Iterable[Tuple[T, T]]
>>> Vector[int]
typing.Iterable[typing.Tuple[int, int]]
>>> Iterable[Tuple[T, U, T]][int]
Traceback (most recent call last):
...
Too few parameters for typing.Iterable[typing.Tuple[~T, ~U, ~T]]; actual 1, expected 2
>>> Iterable[Tuple[T, U, T]][int, str]
typing.Iterable[typing.Tuple[int, str, int]] |
I had forgotten about this change as well... Did it go in without much discussion? |
@JukkaL Most of discussion about this happened in python/typing#115 |
Oops, I think that I wasn't paying much attention to the original discussion then! Yeah, the implementation sounds reasonable -- my biggest reservation is how useful this will be in practice, and it's unclear when we are going to have the bandwidth to add support for this to mypy. |
@JukkaL C = List[Tuple[T, T]]
C[int]
# and
class C(List[Tuple[T, T]]): ...
C[int] Probably you are right that the first form might be of less priority for mypy. Both above forms are already allowed in class C(Tuple[T, T]): ...
class C(Tuple[T, ...]): ...
class C(Callable[[T], T]): ... I would like to submit a PR to I think this is rather positive side effect. If people will ask for this and we decide to support this in mypy, then we would not need to change VecT = Iterable[Tuple[T, T]]
def dilate(vec: VecT, scale: T) -> VecT: ...
def rotate(vec: VecT, angle: T) -> VecT: ...
v: VecT[float] = [] |
@ilevkivskyi Yeah, I'm not opposed to this. By the way, shouldn't your example be written like this:
(Based on what @ddfisher wrote above.) |
Good point! Indeed, it looks much cleaner and |
Re: Vec[T] we should update the PEP example.
Re: subclassing Tuple, let's implement it first and add to the PEP once
implemented. Seeing it as variadic type var is great.
Re: subclassing Callable, I foresee some issues related to the argument
list.
…--Guido (mobile)
|
Fixes #606 as per PEP 484. Now type aliases may be generic, so that the example in PEP works. Generic type aliases are allowed for generic classes, unions, callables, and tuples. For example: Vec = Iterable[Tuple[T, T]] TInt = Tuple[T, int] UInt = Union[T, int] CBack = Callable[..., T] The aliases could be used as specified in PEP 484, e.g. either one specifies all free type variables, or if unspecified they are all substituted by Any, for example: def fun(v: Vec[T]) -> Vec[T]: # Same as Iterable[Tuple[T, T]] ... v1: Vec[int] = [] # Same as Iterable[Tuple[int, int]] v2: Vec = [] # Same as Iterable[Tuple[Any, Any]] v3: Vec[int, int] = [] # Error: Invalid alias, too many type arguments! Generic aliases may be used everywhere where a normal generic type could be used (in annotations, generic base classes etc, and as of recently also in runtime expressions). Nested and chained aliases are allowed, but excessive use of those is discouraged in the docs. Like ordinary (non-generic) aliases, generic ones may be imported from other modules. NOTE: Many examples in the tests and in docs require a recent version of typing.py from python/typing to work at runtime (see #2382). This feature may be used with older versions of typing.py by using type comments or "forward references".
This code should be okay:
It should be equivalent to this:
This was suggested by Guido, "based on the general equivalence in Python of
to
".
The text was updated successfully, but these errors were encountered: