I think the problem comes up more with invariant types than covariant types (if I remember terms correctly).

If the element type doesn't matter, then Sequence[object] is more type-safe than Sequence[Any] because it'll catch if you accidentally access something on the elements (copy/paste mistake, leftover debug code, etc). Since Sequence (and the other read-only iterables iirc) is covariant, you can pass any Sequence in and there isn't really a problem in practice.

But, that trick doesn't really work for MutableSequence because it's invariant. Passing [1, 2, 3] ( MutableSequence[int] ) to MutableSequence[object] is a type error. So, you're forced to do MutableSequence[Any] -- but now you've lost the stricter type checking from above.

The example given on the pytype issue tracker was:

def swap(items: MutableSequence[???], i: int, j: int):
  items[i], items[j] = items[j], items[i]

Which I supposed would generalize to something like, "You can't write a type-safe function that accepts a mutable generic" (a mutable generic should probably be invariant).

I suppose you could work around this by using T and returning T, but this again forces you into a bad design decision (returning a modified input is, at the least, questionable, since it can lead to confusing behavior/incorrect assumptions). Or I guess having a private method that does T->T, with a public wrapper that just accepts T. Or sprinkle casting around. But these really seem like playing games to accomplish what seems like something that should be relatively straight forward.


On Mon, Oct 19, 2020 at 10:14 AM Eric Traut <eric@traut.com> wrote:
This is a timely question. I just implemented a check in pyright for this condition because I found many cases where TypeVars were being used incorrectly within a function signature. I cleaned up a bunch of these cases within our code base thanks to this check.

I'm not sure what you mean by "Any swallows all errors" in the context of a `Sequence[Any]`. Presumably, the type checker would still treat that parameter as a Sequence even though it knows nothing about its contents. I don't see how the presence of a TypeVar would change that behavior. Could you elaborate?

If we think there is value is allowing a single instance of a TypeVar in a signature _and_ we can agree on a well-defined behavior for this case, then I think it makes sense to allow it. Otherwise, I think it's reasonable to emit an error to tell the user that they are using a language feature in a manner that is undefined.

Incidentally, when I implemented this check I noticed that it reported many errors within typeshed stdlib stubs. For example, `_SupportsLessThanT` appears only once in numerous signatures within builtins.pyi.


Eric Traut
Contributor to pyright & pylance
Microsoft Corp.
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
Member address: richardlev@gmail.com