From 56172ebd346d815771764b37360f12958ce334be Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 28 Jul 2016 23:17:30 +0200 Subject: [PATCH 1/4] Added scoping rules for type variables --- pep-0484.txt | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/pep-0484.txt b/pep-0484.txt index 1858e4cc425..28712ae49e7 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -448,6 +448,63 @@ is not generic but implicitly inherits from ``Iterable[Any]``:: Generic metaclasses are not supported. +Scoping rules for type variables +-------------------------------- + +Type variables follow normal name resolution rules. +However, there are some special cases in the static typechecking context: + +* A type variable could be inferred to be equal to different types in a block, + unless this block is a generic class definition block. In such case, + all uses of a type variable are bound to the type that + parameterizes the generic class. Example:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + def fun_1(x: T) -> T: ... # T here + def fun_2(x: T) -> T: ... # and here could be different, see below + + class MyClass(Generic[T]): + def meth_1(self, x: T) -> T: ... # but T here + def meth_2(self, x: T) -> T: ... # and here are always the same + + fun_1(1) # This is OK, T is inferred to be int + fun_2('a') # This is aslo OK, now T is str + + a = MyClass() # type: MyClass[int] + a.meth_1(1) # OK + a.meth_2('a') # This is an error! + +* Free type variables should not appear in the bodies of generic functions. + As well, types of class attributes should not contain free type variables:: + + T = TypeVar('T') + S = TypeVar('S') + class Foo(Generic[T]): + def method(self, x: T, y: S) -> S: # This is an error! + ... + +* A generic class definition that appears inside a generic function + should not use type variables that parametereize the generic function:: + + from typing import List + + def a_fun(x: T) -> None: + + # This is OK + a_list = [] # type: List[T] + ... + + # This is however illegal + class MyGeneric(Generic[T]): + ... + +* A generic class nested in another generic class cannot use the same + type variables, unless the inner class definition is inside a function. + + Instantiating generic classes and type erasure ---------------------------------------------- From 04535d79a6be93df20cecf03ed0534c6eafb0d4d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Fri, 29 Jul 2016 22:21:00 +0200 Subject: [PATCH 2/4] Response to comments --- pep-0484.txt | 55 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index 28712ae49e7..0180cbc7691 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -454,40 +454,69 @@ Scoping rules for type variables Type variables follow normal name resolution rules. However, there are some special cases in the static typechecking context: -* A type variable could be inferred to be equal to different types in a block, - unless this block is a generic class definition block. In such case, - all uses of a type variable are bound to the type that - parameterizes the generic class. Example:: +* A type variable used in a generic function could be inferred to be equal to + different types in the same code block. Example:: from typing import TypeVar, Generic T = TypeVar('T') def fun_1(x: T) -> T: ... # T here - def fun_2(x: T) -> T: ... # and here could be different, see below + def fun_2(x: T) -> T: ... # and here could be different + + fun_1(1) # This is OK, T is inferred to be int + fun_2('a') # This is aslo OK, now T is str + +* A type variable used in a method of a generic class that coincisides + with one of the variables that parameterize this class is always bound + to that variable. Example:: + + from typing import TypeVar, Generic + + T = TypeVar('T') class MyClass(Generic[T]): - def meth_1(self, x: T) -> T: ... # but T here + def meth_1(self, x: T) -> T: ... # T here def meth_2(self, x: T) -> T: ... # and here are always the same - fun_1(1) # This is OK, T is inferred to be int - fun_2('a') # This is aslo OK, now T is str - a = MyClass() # type: MyClass[int] a.meth_1(1) # OK a.meth_2('a') # This is an error! -* Free type variables should not appear in the bodies of generic functions. - As well, types of class attributes should not contain free type variables:: +* A type variable used in a method that does not match any of the variables + that parameterize the class makes this method a generic function in that + variable:: T = TypeVar('T') S = TypeVar('S') class Foo(Generic[T]): - def method(self, x: T, y: S) -> S: # This is an error! + def method(self, x: T, y: S) -> S: + ... + + x = Foo() # type: Foo[int] + y = x.method(0, "abc") # inferred type of y is str + +* Unbound type variables should not appear in the bodies of generic functions, + and in the class bodies appart from method definitions:: + + T = TypeVar('T') + S = TypeVar('S') + + def a_fun(x: T) -> None: + # this is OK + y = [] # type: List[T] + # but below is an error! + y = [] # type: List[S] + + class Bar(Generic[T]): + # this is also an error + an_attr = [] # type: List[S] + + def do_something(x: S) -> S: # this is although OK ... * A generic class definition that appears inside a generic function - should not use type variables that parametereize the generic function:: + should not use type variables that parameterize the generic function:: from typing import List From 91e44a3b6b44cb8f74f14fce340519e551c243b9 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 30 Jul 2016 00:52:35 +0200 Subject: [PATCH 3/4] A random typo --- pep-0484.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-0484.txt b/pep-0484.txt index 0180cbc7691..214477d857b 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -1383,7 +1383,7 @@ from ``UserId`` where ``int`` is expected. Examples:: num = UserId(5) + 1 # type: int -``NewType`` accepts only one argument that shoud be a proper class, +``NewType`` accepts only one argument that should be a proper class, i.e., not a type construct like ``Union``, etc. The function returned by ``NewType`` accepts only one argument; this is equivalent to supporting only one constructor accepting an instance of the base class (see above). From 6f2456ebf3f981eed57c4cde926a9e50301d4e30 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 30 Jul 2016 01:31:00 +0200 Subject: [PATCH 4/4] second round of comments --- pep-0484.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pep-0484.txt b/pep-0484.txt index 214477d857b..491e31024f8 100644 --- a/pep-0484.txt +++ b/pep-0484.txt @@ -497,7 +497,7 @@ However, there are some special cases in the static typechecking context: y = x.method(0, "abc") # inferred type of y is str * Unbound type variables should not appear in the bodies of generic functions, - and in the class bodies appart from method definitions:: + or in the class bodies apart from method definitions:: T = TypeVar('T') S = TypeVar('S') @@ -512,7 +512,7 @@ However, there are some special cases in the static typechecking context: # this is also an error an_attr = [] # type: List[S] - def do_something(x: S) -> S: # this is although OK + def do_something(x: S) -> S: # this is OK though ... * A generic class definition that appears inside a generic function @@ -1383,11 +1383,11 @@ from ``UserId`` where ``int`` is expected. Examples:: num = UserId(5) + 1 # type: int -``NewType`` accepts only one argument that should be a proper class, -i.e., not a type construct like ``Union``, etc. The function returned -by ``NewType`` accepts only one argument; this is equivalent to supporting -only one constructor accepting an instance of the base class (see above). -Example:: +``NewType`` accepts exactly two arguments: a name for the new unique type, +and a base class. The latter should be a proper class, i.e., +not a type construct like ``Union``, etc. The function returned by ``NewType`` +accepts only one argument; this is equivalent to supporting only one +constructor accepting an instance of the base class (see above). Example:: class PacketId: def __init__(self, major: int, minor: int) -> None: