Skip to content

PEP draft for +T and -T syntax#2

Closed
jorenham wants to merge 28 commits intomasterfrom
plusT
Closed

PEP draft for +T and -T syntax#2
jorenham wants to merge 28 commits intomasterfrom
plusT

Conversation

@jorenham
Copy link
Copy Markdown
Owner

@jorenham jorenham commented Sep 7, 2021

PR for easy feedback

@jorenham jorenham linked an issue Sep 7, 2021 that may be closed by this pull request
@jorenham jorenham marked this pull request as draft September 7, 2021 22:25
Comment thread pep-9999.rst
Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst Outdated


This example is valid, but using ``T_co`` or ``T_contra`` instead of
``T`` here does not make sense, and type checkers do not allow is.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My experience is that type checkers ignore variance in most contexts where it does not apply, instead of reporting an error.

For example, all of the following definitions type check with mypy 0.902

def consume_one(thing: T_co) -> None: ...

def consume(things: Iterable[T_contra]) -> None: ...

Callback = Callable[[T_co], T_contra]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that (at least) mypy does not allow this

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mypy does not allow a contravariant type variable in the return type annotation, but it does allow all of the above :)

Comment thread pep-9999.rst
Comment thread pep-9999.rst
Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst
Comment thread pep-9999.rst
Comment thread pep-9999.rst
Comment thread pep-9999.rst
Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst Outdated
@jorenham jorenham marked this pull request as ready for review October 17, 2021 15:48
@jorenham
Copy link
Copy Markdown
Owner Author

Hi @JelleZijlstra , it's been a while, but would you mind giving this another look? Also, do you think it a good idea for me to get started on the reference implementations at this point?

@JelleZijlstra
Copy link
Copy Markdown

Yes, I'll take another look today or tomorrow! And yes, I think a reference implementation (runtime + mypy) would be useful.

Comment thread pep-9999.rst Outdated
Copy link
Copy Markdown

@JelleZijlstra JelleZijlstra left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a few more comments.

Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst
Comment on lines +25 to +27
This PEP uses some terms to refer to specific concepts related to Python
typing, or type systems in general. For the sake of clarity, this PEP
considers the following terms and definitions:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section looks scarier than it needs to be to readers: all of these terms are standard in typing, not specific to this PEP. But this section makes it look like you need PEP-specific definitions for all these terms.

It doesn't look like the Python docs have a glossary just yet, but that could be a useful addition.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think I should remove it alltogether, or restructure (a part of) it and rename the section to e.g. "introduction", or "background"?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest renaming it to the bottom and removing the introductory paragraph.

Comment thread pep-9999.rst
user-defined generic types.

type parameter
Generic types can have one or multiple type parameters. For user-defined
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can drop "For user-defined generic types". TypeVars also used for builtin generics, the TypeVars are just only in typeshed.

Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst Outdated
Comment thread pep-9999.rst

In ``SimpleFunction``, ``T_co`` or ``T_contra`` are used to annotate
its method as well. This is required because the variance is associated
with the individual type variables. But the method signature is unrelated
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is true: the method signature does have something to do with the variance of the typevars, because the reason they need to be contravariant and covariant is because of the places where they appear in the signature.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're right @JelleZijlstra . I was trying to point out that associating variance with a type variable should only happen once, and that currently that isn't possible, which is pretty confusing. But I'm not sure whether it's technically incorrect to mark a typevar as variant within a method, or whether it's just redundant. What do you think? Should I leave this example out? And do you think it'd help illustrating how the current variance api is confusing by adding similar examples of how to use it incorrectly?

Comment thread pep-9999.rst
class SupportsPartialOrder(typing.Protocol[-T]):
def __le__(self, other: T): ...

This syntax is inspired by Scala programming language [1]_.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This syntax is inspired by Scala programming language [1]_.
This syntax is inspired by the Scala programming language [1]_.

You can also mention that this is already being used in the repr() of TypeVars.

Comment thread pep-9999.rst

- it is not already co- or contravariant
- it has no type constraints
- it has no type bound
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variance is actually meaningful on TypeVars with a bound: https://mypy-play.net/?mypy=latest&python=3.10&gist=f72bac261a2148ca8ae6d6670dfb3459.

And similarly for value restrictions, see this example: https://mypy-play.net/?mypy=latest&python=3.10&gist=5a4f16dd13bd4b18a7c2323ba464f627. Mypy allows this but I think that's wrong; pyright gives an error on the last line. I opened python/mypy#11379.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variance with value restriction is counter-intuitive, and I suspect it might actually be best to forbid that.

PEP-484 uses the following example

def concat(x: AnyStr, y: AnyStr) -> AnyStr:
    return x + y

class MyStr(str): ...

x = concat(MyStr('apple'), MyStr('pie'))

and says that the type bound to AnyStr would be str and not MyStr. This sounds exotic and tbh as a user I would be very confused by the fact that the type variable was bound to a type different from the type of the argument.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JelleZijlstra I see how it's useful for covariant types to have bounds or value restrictions. But a contravariant type with bounds is strange one. In PEP484 it's explicitly called an "upper bound", but that would make bounded contravariant types useless, since they can only "go up". So without lower bounds being a thing, I think that we either

  • only allow covariant typevars to have bounds/value restrictions,
  • forbid bounds and restrictions on variant typevars allthogether for now,
  • change the definitions so that contravariant typevars "flip" the bounds/restrictions,
  • or leave it (explicitly) unspecified.

I think the last option would the cleanest, perhaps this issue will be more appropriate in e.g. your T <= int PEP @JelleZijlstra .

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PEP should stay purely syntactic and not change restrictions on when covariant + bounds/constraints is allowed. That will keep it more focused. If we want changes to what kind of semantics are allowed, that can be discussed separately.

Comment thread pep-9999.rst
and ``typing.ParamSpec`` instances, iff

- it is not already co- or contravariant
- it has no type constraints
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In PEP 484 that isn't mentioned, and refer to it as "constraints" instead. And in cpython, they are stored in TypeVar's __constraints__ attribute as a tuple. So wouldn't it make more sense to talk about "contraints" for TypeVar(..., A, B) and "bound(s)" for TypeVar(..., bound=A)?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I guess mypy's doocs are the odd one out there.

jorenham and others added 6 commits October 25, 2021 05:44
Co-authored-by: Jelle Zijlstra <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
Co-authored-by: Jelle Zijlstra <[email protected]>
@jorenham jorenham closed this Oct 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type parameter variance with +T and -T

3 participants