[Python-ideas] Type Hinting Kick-off

Eugene Toder eltoder at gmail.com
Thu Dec 25 01:50:09 CET 2014

Guido van Rossum <guido at ...> writes:
> A few months ago we had a long discussion about type hinting. I've 
thought a
> lot more about this. I've written up what I think is a decent "theory"
> document -- writing it down like  this certainly helped *me* get a lot 
> clarity about some of the important 

(I apologize in advance if some of this was covered in previous 

1. Since there's the Union type, it's also natural to have the 
type. A class is a subclass of Intersection[t1, t2, ...] if it's a 
of all t1, t2 etc. The are 2 main uses of the Intersection type:

a) Require that an argument implements multiple interfaces:

class Foo:
    def foo(self): ...

class Bar:
    def bar(self): ...

def fooItWithABar(obj: Intersection[Foo, Bar]): ...

b) Write the type of an overloaded function:

def foo(x: str) -> str: ...
def foo(x: bytes) -> bytes: ...

foo # type: Intersection[Callable[[str], str], Callable[[bytes], bytes]]

2. Constrained type variables (Var('Y', t1, t2, ...)) have a very 

a) "subclasses of t1 etc. are replaced by the most-derived base class 
among t1
This defeats the very common use of constrained type variables: have a 
preserving function limited to classes inherited from a common base. 
E.g. say
we have a function:

def relocate(e: Employee) -> Employee: ...

The function actually always returns an object of the same type as the
argument, so we want to write a more precise type. We usually do it like 

XEmployee = Var('XEmployee', Employee)

def relocate(e: XEmployee) -> XEmployee: ...

This won't work with the definition from the proposal.

b) Multiple constraints introduce an implicit Union. I'd argue that type
variables are more commonly constrained by an Intersection rather than a
Union. So it will be more useful if given this definition Y has to be
compatible with all of t1, t2 etc, rather than just one of them.
Alternatively, this can be always spelled out explicitly: 
    Y1 = Var('Y1', Union[t1, t2, ...])
    Y2 = Var('Y2', Intersection[t1, t2, ...])


3. The names Union and Intersection are standard terminology in type 
but may not be familiar to many Python users. Names like AnyOf[] and 
can be more intuitive.

4. Similar to allowing None to mean type(None) it's nice to have 
    (t1, t2, ...) == Tuple[t1, t2, ...]
    [t1] == List[t1]
    {t1: t2} == Dict[t1, t2]
    {t1} == Set[t1]

The last 3 can be Sequence[t1], Mapping[t1, t2] and collections.Set[t1] 
if we
want to encourage the use of abstract types.

5. Using strings for forward references can be messy in case of 
parsing of brackets etc in the string will be needed. I propose explicit
forward declarations:

C = Declare('C')
class C(Generic[X]):
    def foo(self, other: C[X]): ...
    def bar(self, other: C[Y]): ...

6. On the other hand, using strings for unconstrained type variables is 
handy, and doesn't share the same problem:

def head(xs: List['T']) -> 'T': ...


More information about the Python-ideas mailing list