Skip to content

[WIP] Add scoping rules for type variables to PEP 484 #63

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

Merged
merged 4 commits into from
Jul 29, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 91 additions & 5 deletions pep-0484.txt
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,92 @@ 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 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

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: ... # T here
def meth_2(self, x: T) -> T: ... # and here are always the same

a = MyClass() # type: MyClass[int]
a.meth_1(1) # OK
a.meth_2('a') # This is an error!

* 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:
...

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,
or in the class bodies apart 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 OK though
...

* A generic class definition that appears inside a generic function
should not use type variables that parameterize 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
----------------------------------------------

Expand Down Expand Up @@ -1297,11 +1383,11 @@ from ``UserId`` where ``int`` is expected. Examples::

num = UserId(5) + 1 # type: int

``NewType`` accepts only one argument that shoud 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:
Expand Down