Skip to content
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

Rename typevar('T', values=(t1, t2, ...)) to TypeVar('T', t1, t2, ...) #539

Closed
gvanrossum opened this issue Dec 19, 2014 · 7 comments
Closed

Comments

@gvanrossum
Copy link
Member

In the typehinting PEP the proposal is to use Var() instead of typevar(), and to make the constraints just varargs, rather than a keyword parameter (and why would the keyword be 'values'?).

The semantics of typevar() are fine. The PEP currently seems to state that two Var() calls with the same name generate equivalent variables, whereas mypy treats every typevar() as unique. It also enforces that the typevar is assigned to a variable with the same name as the typevar argument. I think the mypy rules are better, and easier to implement, so I prefer those.

See also python/typing#1 for the first issue (renaming, signature change) and python/typing#29 for the second (uniqueness).

@JukkaL
Copy link
Collaborator

JukkaL commented Dec 20, 2014

Renaming typevar to Var sounds okay. I initially thought that Var is not very googleable, but googling for 'var python' actually doesn't bring up many relevant hits, so it should be okay.

The values keyword argument is pretty awkward, agreed.

One thing I'd like to point out is that if we use Var for type variables, it would probably be confusing to use var x: type for declaring variables (this is a highly speculative syntax extension for a post-3.5 Python version that we've discussed).

Before deciding the syntax for Var constraints, I'd like to see a proposal for co/contravariance, as it might involve changes to Var (see python/typing#2).

@gvanrossum
Copy link
Member Author

On the python-ideas thread I started just before Xmas there was quite a bit of push-back on the current semantics of typevar constraints from Eugene Toder. (Starting here: https://mail.python.org/pipermail/python-ideas/2014-December/030482.html .) Basically he claims that the more common type of constrained type variable should be covariant. I think this example would show it:

class Employee: ...
class Manager(Employee): ...
EMP = Var('EMP', Employee)
def foo(e: EMP) -> EMP: ...
def main():
    m1 = Manager()
    m2 = foo(m1)
    # Now m2 is known to be a Manager, not just an Employee

I am skeptical of his claim, but I am not sufficiently familiar with other type systems to refute it. All I can say is, if foo() receives an instance of an unknown subclass of Employee, how can it create an instance of the appropriate class (if it's not just returning the argument)? To which Eugene replied that in such cases the Employee class should require that ever subclass has a copying factory (https://mail.python.org/pipermail/python-ideas/2014-December/030488.html), which hardly seems a general solution. The discussion went fractal after that.

Assuming we resolve that particular issue by ignoring or refuting it, we seem to have two issues on left the table before we can choose a syntax:

  • Possible confusion if in a future Python release we add "var" as a keyword for variable declarations
  • Syntax for choosing covariant type variables, and choosing the default behavior (invariant is proposed)

I propose to use TypeVar(name: str, *constraints: type, **kwds) where *constraints is a list of zero or more types to be used as constraints (lower bounds?) and **kwds is an open-ended set of keyword args that can be used to modify the semantics. I think invariant is a good default behavior, and I personally have no problem with using covariant=True to indicate covariant behavior. I propose CapWords style for the name because in my prototype this is actually a metaclass instantiation call. This also allows mypy to transition gradually by recognizing both typevar and TypeVar with different signatures.

@JukkaL
Copy link
Collaborator

JukkaL commented Jan 7, 2015

Eugene's examples are more relevant than your example since they use F-bounded polymorphism (see below), and I agree that it would be nice to be able to support them.

Another term used for type variable subtype constraints is bound, and an F-bound contains a constrained type variable in the bound. Here is another example motivated by builtins (CMP and min are the relevant parts; Comparable is an ordinary generic ABC):

class Comparable(AbstractGeneric[T]):
    @abstractmethod
    def __lt__(x, other: T) -> bool: pass
    @abstractmethod
    def __gt__(x, other: T) -> bool: pass
    ...

CMP = Var('CMP', Comparable['CMP'])

def min(x: CMP, y: CMP) -> CMP:
    if x < y:
        return x
    else:
        return y

min(1, 2)  # Ok
min('x', 'y')  # Ok
min(1, 'x')  # Error

(I'm assuming that int and str are subclasses of Comparable.)

This could also be useful for things like sort and seems genuinely useful.

He also proposes something called explicit protocols, which look interesting. I'm not sure if they solve exactly the same problem as the current mypy approach, since I don't seem anything restricting a type like AnyStr / StringLike to only include the types str and bytes.

@gvanrossum
Copy link
Member Author

Wow, there are a lot of different ideas here. Maybe we need separate issues to discuss them? Here or in the typehinting repo? (In my defense, Eugene also supplied the Employee example, in the first of the two messages I linked to; he used XEmployee = Var('XEmployee', Employee).)

  • Rename typevar to Var, or to TypeVar
  • "regular" bounds (as opposed to the kind that AnyStr uses; what should the terminology be?)
  • F-bounded polymorphism
  • explicit protocols
  • anything else I missed?

@ambv
Copy link
Contributor

ambv commented Jan 7, 2015

Renamed Var to TypeVar in b7d16de to leave space for variable types in the future.

@ambv
Copy link
Contributor

ambv commented Jan 7, 2015

As for explicit protocols, there are some competing (as in: not easily composable) standards here:

  1. ABCs with @AbstractMethod
  2. Zope interfaces
  3. https://pypi.python.org/pypi/characteristic/

etc. etc.

As ABCs are built-in, it seems natural to suggest that they should be used for defining interfaces. However, I understand that static analysis (so, the type checker) might not be able to process every abstract class in general.

@gvanrossum
Copy link
Member Author

Łukasz, could you move the discussion on protocols to python/typing#11 ?

@JukkaL JukkaL changed the title Rename typevar('T', values=(t1, t2, ...)) to Var('T', t1, t2, ...) Rename typevar('T', values=(t1, t2, ...)) to TypeVar('T', t1, t2, ...) Jan 17, 2015
@JukkaL JukkaL added the priority label Mar 9, 2015
@JukkaL JukkaL closed this as completed in 28a9fcd Mar 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants